From 31fc36b3430d804e835ede2b936df46fef51467e Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Wed, 28 Dec 2022 10:31:48 -0800 Subject: [PATCH 01/18] Clean up .idea folder in assorted places. --- .gitignore | 17 +++-------------- .../ch4_final_video_collector/.idea/.gitignore | 8 -------- .../ch4_final_video_collector/.idea/.name | 1 - .../ch5_starter_video_collector/.idea/.name | 1 - .../ch6_final_video_collector/.idea/.name | 1 - .../ch6_starter_video_collector/.idea/.name | 1 - .../ch7_final_video_collector/.idea/.name | 1 - .../ch7_starter_video_collector/.idea/.name | 1 - code/starter_video_collector/.idea/.name | 1 - 9 files changed, 3 insertions(+), 29 deletions(-) delete mode 100644 code/ch4_app/ch4_final_video_collector/.idea/.gitignore delete mode 100644 code/ch4_app/ch4_final_video_collector/.idea/.name delete mode 100644 code/ch5_partials/ch5_starter_video_collector/.idea/.name delete mode 100644 code/ch6_active_search/ch6_final_video_collector/.idea/.name delete mode 100644 code/ch6_active_search/ch6_starter_video_collector/.idea/.name delete mode 100644 code/ch7_infinite_scroll/ch7_final_video_collector/.idea/.name delete mode 100644 code/ch7_infinite_scroll/ch7_starter_video_collector/.idea/.name delete mode 100644 code/starter_video_collector/.idea/.name diff --git a/.gitignore b/.gitignore index a529c48..9960643 100644 --- a/.gitignore +++ b/.gitignore @@ -127,17 +127,6 @@ dmypy.json # Pyre type checker .pyre/ -code/starter_video_collector/.idea/.gitignore -code/starter_video_collector/.idea/misc.xml -code/starter_video_collector/.idea/modules.xml -code/starter_video_collector/.idea/starter_video_collector.iml -code/starter_video_collector/.idea/vcs.xml -code/starter_video_collector/.idea/inspectionProfiles/profiles_settings.xml -code/starter_video_collector/.idea/inspectionProfiles/Project_Default.xml -code/ch4_app/ch4_final_video_collector/.idea/ch4_final_video_collector.iml -code/ch4_app/ch4_final_video_collector/.idea/modules.xml -code/ch4_app/ch4_final_video_collector/.idea/* -code/starter_video_collector/.idea/* -code/ch4_app/ch4_final_video_collector/.idea/* -code/starter_video_collector/.idea/* -code/ch7_infinite_scroll/ch7_final_video_collector/.idea + +# Pycharm project folder +.idea/* diff --git a/code/ch4_app/ch4_final_video_collector/.idea/.gitignore b/code/ch4_app/ch4_final_video_collector/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/code/ch4_app/ch4_final_video_collector/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/code/ch4_app/ch4_final_video_collector/.idea/.name b/code/ch4_app/ch4_final_video_collector/.idea/.name deleted file mode 100644 index f50865d..0000000 --- a/code/ch4_app/ch4_final_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch4] \ No newline at end of file diff --git a/code/ch5_partials/ch5_starter_video_collector/.idea/.name b/code/ch5_partials/ch5_starter_video_collector/.idea/.name deleted file mode 100644 index 2b6ae51..0000000 --- a/code/ch5_partials/ch5_starter_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch5] \ No newline at end of file diff --git a/code/ch6_active_search/ch6_final_video_collector/.idea/.name b/code/ch6_active_search/ch6_final_video_collector/.idea/.name deleted file mode 100644 index c341c0d..0000000 --- a/code/ch6_active_search/ch6_final_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch6] \ No newline at end of file diff --git a/code/ch6_active_search/ch6_starter_video_collector/.idea/.name b/code/ch6_active_search/ch6_starter_video_collector/.idea/.name deleted file mode 100644 index c341c0d..0000000 --- a/code/ch6_active_search/ch6_starter_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch6] \ No newline at end of file diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/.idea/.name b/code/ch7_infinite_scroll/ch7_final_video_collector/.idea/.name deleted file mode 100644 index edbb527..0000000 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch7] \ No newline at end of file diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/.idea/.name b/code/ch7_infinite_scroll/ch7_starter_video_collector/.idea/.name deleted file mode 100644 index edbb527..0000000 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [ch7] \ No newline at end of file diff --git a/code/starter_video_collector/.idea/.name b/code/starter_video_collector/.idea/.name deleted file mode 100644 index b25f2ca..0000000 --- a/code/starter_video_collector/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -HTMX Video Collector [starter project] \ No newline at end of file From fbaf0234d8619601bf7a44ed201a5ebdb0ec83dc Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Wed, 28 Dec 2022 10:32:14 -0800 Subject: [PATCH 02/18] Use pip-tools to create pinned requirements for each chapter's projects for greater stability. --- .../ch4_final_video_collector/requirements.in | 6 +++ .../requirements.txt | 37 ++++++++++++++--- .../requirements.in | 6 +++ .../requirements.txt | 37 ++++++++++++++--- .../ch5_final_video_collector/requirements.in | 7 ++++ .../requirements.txt | 41 +++++++++++++++---- .../requirements.in | 6 +++ .../requirements.txt | 37 ++++++++++++++--- .../ch6_final_video_collector/requirements.in | 7 ++++ .../requirements.txt | 41 +++++++++++++++---- .../requirements.in | 7 ++++ .../requirements.txt | 41 +++++++++++++++---- .../ch7_final_video_collector/requirements.in | 7 ++++ .../requirements.txt | 41 +++++++++++++++---- .../requirements.in | 7 ++++ .../requirements.txt | 41 +++++++++++++++---- code/starter_video_collector/requirements.in | 6 +++ code/starter_video_collector/requirements.txt | 37 ++++++++++++++--- requirements.txt | 34 +++++++++++++++ 19 files changed, 387 insertions(+), 59 deletions(-) create mode 100644 code/ch4_app/ch4_final_video_collector/requirements.in create mode 100644 code/ch4_app/ch4_starter_video_collector/requirements.in create mode 100644 code/ch5_partials/ch5_final_video_collector/requirements.in create mode 100644 code/ch5_partials/ch5_starter_video_collector/requirements.in create mode 100644 code/ch6_active_search/ch6_final_video_collector/requirements.in create mode 100644 code/ch6_active_search/ch6_starter_video_collector/requirements.in create mode 100644 code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in create mode 100644 code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in create mode 100644 code/starter_video_collector/requirements.in create mode 100644 requirements.txt diff --git a/code/ch4_app/ch4_final_video_collector/requirements.in b/code/ch4_app/ch4_final_video_collector/requirements.in new file mode 100644 index 0000000..f2e5eba --- /dev/null +++ b/code/ch4_app/ch4_final_video_collector/requirements.in @@ -0,0 +1,6 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 diff --git a/code/ch4_app/ch4_final_video_collector/requirements.txt b/code/ch4_app/ch4_final_video_collector/requirements.txt index f2e5eba..ed68e16 100644 --- a/code/ch4_app/ch4_final_video_collector/requirements.txt +++ b/code/ch4_app/ch4_final_video_collector/requirements.txt @@ -1,6 +1,31 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja2==3.1.2 + # via + # -r requirements.in + # flask +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.in b/code/ch4_app/ch4_starter_video_collector/requirements.in new file mode 100644 index 0000000..f2e5eba --- /dev/null +++ b/code/ch4_app/ch4_starter_video_collector/requirements.in @@ -0,0 +1,6 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.txt b/code/ch4_app/ch4_starter_video_collector/requirements.txt index f2e5eba..ed68e16 100644 --- a/code/ch4_app/ch4_starter_video_collector/requirements.txt +++ b/code/ch4_app/ch4_starter_video_collector/requirements.txt @@ -1,6 +1,31 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja2==3.1.2 + # via + # -r requirements.in + # flask +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.in b/code/ch5_partials/ch5_final_video_collector/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/code/ch5_partials/ch5_final_video_collector/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.txt b/code/ch5_partials/ch5_final_video_collector/requirements.txt index 6ec68a8..0cd62ed 100644 --- a/code/ch5_partials/ch5_final_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_final_video_collector/requirements.txt @@ -1,7 +1,34 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 -jinja-partials +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.in b/code/ch5_partials/ch5_starter_video_collector/requirements.in new file mode 100644 index 0000000..f2e5eba --- /dev/null +++ b/code/ch5_partials/ch5_starter_video_collector/requirements.in @@ -0,0 +1,6 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.txt b/code/ch5_partials/ch5_starter_video_collector/requirements.txt index f2e5eba..ed68e16 100644 --- a/code/ch5_partials/ch5_starter_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_starter_video_collector/requirements.txt @@ -1,6 +1,31 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja2==3.1.2 + # via + # -r requirements.in + # flask +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.in b/code/ch6_active_search/ch6_final_video_collector/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/code/ch6_active_search/ch6_final_video_collector/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.txt b/code/ch6_active_search/ch6_final_video_collector/requirements.txt index 6ec68a8..0cd62ed 100644 --- a/code/ch6_active_search/ch6_final_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_final_video_collector/requirements.txt @@ -1,7 +1,34 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 -jinja-partials +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.in b/code/ch6_active_search/ch6_starter_video_collector/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/code/ch6_active_search/ch6_starter_video_collector/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt index 6ec68a8..0cd62ed 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt @@ -1,7 +1,34 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 -jinja-partials +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt index 6ec68a8..0cd62ed 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt @@ -1,7 +1,34 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 -jinja-partials +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt index 6ec68a8..0cd62ed 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt @@ -1,7 +1,34 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 -jinja-partials +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/code/starter_video_collector/requirements.in b/code/starter_video_collector/requirements.in new file mode 100644 index 0000000..f2e5eba --- /dev/null +++ b/code/starter_video_collector/requirements.in @@ -0,0 +1,6 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 diff --git a/code/starter_video_collector/requirements.txt b/code/starter_video_collector/requirements.txt index f2e5eba..ed68e16 100644 --- a/code/starter_video_collector/requirements.txt +++ b/code/starter_video_collector/requirements.txt @@ -1,6 +1,31 @@ -flask -Werkzeug -pydantic -more_itertools -MarkupSafe -Jinja2 +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja2==3.1.2 + # via + # -r requirements.in + # flask +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0cd62ed --- /dev/null +++ b/requirements.txt @@ -0,0 +1,34 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +click==8.1.3 + # via flask +flask==2.2.2 + # via -r requirements.in +itsdangerous==2.1.2 + # via flask +jinja-partials==0.1.1 + # via -r requirements.in +jinja2==3.1.2 + # via + # -r requirements.in + # flask + # jinja-partials +markupsafe==2.1.1 + # via + # -r requirements.in + # jinja2 + # werkzeug +more-itertools==9.0.0 + # via -r requirements.in +pydantic==1.10.2 + # via -r requirements.in +typing-extensions==4.4.0 + # via pydantic +werkzeug==2.2.2 + # via + # -r requirements.in + # flask From ce9c5bf5caf8a5f907279c7b6718eca8696e4c76 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Thu, 29 Dec 2022 21:06:42 -0800 Subject: [PATCH 03/18] ignore pycharm files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9960643..6deed5a 100644 --- a/.gitignore +++ b/.gitignore @@ -129,4 +129,5 @@ dmypy.json .pyre/ # Pycharm project folder -.idea/* +.idea/ +.idea From 58c3979d6082a9d2e0d5e88dfd0a73ac9874f936 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 16 Jan 2023 09:37:31 -0800 Subject: [PATCH 04/18] Fix bug which didn't (or always) transforms the form dictionary in request dict. Fixes #4 --- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- .../ch7_final_video_collector/requirements.txt | 8 ++++---- .../infrastructure/request_dict.py | 10 +++++----- .../infrastructure/request_dict.py | 10 +++++----- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/code/ch4_app/ch4_final_video_collector/infrastructure/request_dict.py b/code/ch4_app/ch4_final_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch4_app/ch4_final_video_collector/infrastructure/request_dict.py +++ b/code/ch4_app/ch4_final_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch4_app/ch4_starter_video_collector/infrastructure/request_dict.py b/code/ch4_app/ch4_starter_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch4_app/ch4_starter_video_collector/infrastructure/request_dict.py +++ b/code/ch4_app/ch4_starter_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch5_partials/ch5_final_video_collector/infrastructure/request_dict.py b/code/ch5_partials/ch5_final_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch5_partials/ch5_final_video_collector/infrastructure/request_dict.py +++ b/code/ch5_partials/ch5_final_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch5_partials/ch5_starter_video_collector/infrastructure/request_dict.py b/code/ch5_partials/ch5_starter_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch5_partials/ch5_starter_video_collector/infrastructure/request_dict.py +++ b/code/ch5_partials/ch5_starter_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch6_active_search/ch6_final_video_collector/infrastructure/request_dict.py b/code/ch6_active_search/ch6_final_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch6_active_search/ch6_final_video_collector/infrastructure/request_dict.py +++ b/code/ch6_active_search/ch6_final_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch6_active_search/ch6_starter_video_collector/infrastructure/request_dict.py b/code/ch6_active_search/ch6_starter_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/infrastructure/request_dict.py +++ b/code/ch6_active_search/ch6_starter_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/request_dict.py b/code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/request_dict.py +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt index 0cd62ed..acac8f1 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask @@ -24,7 +24,7 @@ markupsafe==2.1.1 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.4 # via -r requirements.in typing-extensions==4.4.0 # via pydantic diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/infrastructure/request_dict.py b/code/ch7_infinite_scroll/ch7_starter_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/infrastructure/request_dict.py +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string diff --git a/code/starter_video_collector/infrastructure/request_dict.py b/code/starter_video_collector/infrastructure/request_dict.py index d7ab10d..4098c1b 100644 --- a/code/starter_video_collector/infrastructure/request_dict.py +++ b/code/starter_video_collector/infrastructure/request_dict.py @@ -14,16 +14,16 @@ def __getattr__(self, key): def create(default_val=None, **route_args) -> RequestDictionary: request = flask.request - # Adding this retro actively. Some folks are experiencing issues where they + # Adding this retroactively. Some folks are experiencing issues where they # are getting a list rather than plain dict. I think it's from multiple # entries in the multidict. This should fix it. args = request.args - if isinstance(request.args, MultiDict): - args = request.args.to_dict() + if isinstance(args, MultiDict): + args = args.to_dict() form = request.form - if isinstance(request.args, MultiDict): - form = request.form.to_dict() + if isinstance(form, MultiDict): + form = form.to_dict() data = { **args, # The key/value pairs in the URL query string From 93bd13acad177804e14e38dca01297ca44f3be8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:08:07 +0000 Subject: [PATCH 05/18] Bump werkzeug in /code/ch7_infinite_scroll/ch7_starter_video_collector Bumps [werkzeug](https://github.com/pallets/werkzeug) from 2.2.2 to 2.2.3. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/2.2.2...2.2.3) --- updated-dependencies: - dependency-name: werkzeug dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../ch7_starter_video_collector/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt index 0cd62ed..0763adc 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt @@ -28,7 +28,7 @@ pydantic==1.10.2 # via -r requirements.in typing-extensions==4.4.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask From b83af1fc74b148969f624ecc8360ce455d02c3b5 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Fri, 24 Feb 2023 10:10:54 -0800 Subject: [PATCH 06/18] bump dependencies (werkzeug had an issue). --- .../ch7_final_video_collector/requirements.txt | 14 +++++++------- .../ch7_starter_video_collector/requirements.txt | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt index acac8f1..366e3db 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt @@ -1,12 +1,12 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: # # pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +17,18 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.4 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt index 0cd62ed..366e3db 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +17,18 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask From 8ff7f65d916f6b8d2453f94d103a31cf2824ba01 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Fri, 24 Feb 2023 10:13:52 -0800 Subject: [PATCH 07/18] Bump a few versions due to minor security issues. --- .../ch4_final_video_collector/requirements.txt | 12 ++++++------ .../ch4_starter_video_collector/requirements.txt | 12 ++++++------ .../ch5_final_video_collector/requirements.txt | 12 ++++++------ .../ch5_starter_video_collector/requirements.txt | 12 ++++++------ .../ch6_final_video_collector/requirements.txt | 12 ++++++------ .../ch6_starter_video_collector/requirements.txt | 12 ++++++------ code/starter_video_collector/requirements.txt | 12 ++++++------ 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/code/ch4_app/ch4_final_video_collector/requirements.txt b/code/ch4_app/ch4_final_video_collector/requirements.txt index ed68e16..112b1af 100644 --- a/code/ch4_app/ch4_final_video_collector/requirements.txt +++ b/code/ch4_app/ch4_final_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +14,18 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.txt b/code/ch4_app/ch4_starter_video_collector/requirements.txt index ed68e16..112b1af 100644 --- a/code/ch4_app/ch4_starter_video_collector/requirements.txt +++ b/code/ch4_app/ch4_starter_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +14,18 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.txt b/code/ch5_partials/ch5_final_video_collector/requirements.txt index 0cd62ed..366e3db 100644 --- a/code/ch5_partials/ch5_final_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_final_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +17,18 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.txt b/code/ch5_partials/ch5_starter_video_collector/requirements.txt index ed68e16..112b1af 100644 --- a/code/ch5_partials/ch5_starter_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_starter_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +14,18 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.txt b/code/ch6_active_search/ch6_final_video_collector/requirements.txt index 0cd62ed..366e3db 100644 --- a/code/ch6_active_search/ch6_final_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_final_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +17,18 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt index 0cd62ed..366e3db 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +17,18 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask diff --git a/code/starter_video_collector/requirements.txt b/code/starter_video_collector/requirements.txt index ed68e16..112b1af 100644 --- a/code/starter_video_collector/requirements.txt +++ b/code/starter_video_collector/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # click==8.1.3 # via flask -flask==2.2.2 +flask==2.2.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +14,18 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.1 +markupsafe==2.1.2 # via # -r requirements.in # jinja2 # werkzeug more-itertools==9.0.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==1.10.5 # via -r requirements.in -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask From 5111f233b8172125f04661df087ccd03afa8a024 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:15:17 +0000 Subject: [PATCH 08/18] Bump werkzeug from 2.2.2 to 2.2.3 Bumps [werkzeug](https://github.com/pallets/werkzeug) from 2.2.2 to 2.2.3. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/2.2.2...2.2.3) --- updated-dependencies: - dependency-name: werkzeug dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0cd62ed..0763adc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ pydantic==1.10.2 # via -r requirements.in typing-extensions==4.4.0 # via pydantic -werkzeug==2.2.2 +werkzeug==2.2.3 # via # -r requirements.in # flask From d9cc19e8d898787cbbfa4b820339708a8350f923 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 28 Aug 2023 09:17:53 -0700 Subject: [PATCH 09/18] Upgrade to be compatible with Pydantic v2. --- .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- .../requirements.txt | 22 ++++++++++------ .../models/video_model.py | 2 +- code/starter_video_collector/requirements.txt | 25 +++++++++++++------ requirements.in | 7 ++++++ requirements.txt | 24 ++++++++++++------ 20 files changed, 170 insertions(+), 80 deletions(-) create mode 100644 requirements.in diff --git a/code/ch4_app/ch4_final_video_collector/models/video_model.py b/code/ch4_app/ch4_final_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch4_app/ch4_final_video_collector/models/video_model.py +++ b/code/ch4_app/ch4_final_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch4_app/ch4_final_video_collector/requirements.txt b/code/ch4_app/ch4_final_video_collector/requirements.txt index 112b1af..71e7465 100644 --- a/code/ch4_app/ch4_final_video_collector/requirements.txt +++ b/code/ch4_app/ch4_final_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +18,22 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch4_app/ch4_starter_video_collector/models/video_model.py b/code/ch4_app/ch4_starter_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch4_app/ch4_starter_video_collector/models/video_model.py +++ b/code/ch4_app/ch4_starter_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.txt b/code/ch4_app/ch4_starter_video_collector/requirements.txt index 112b1af..71e7465 100644 --- a/code/ch4_app/ch4_starter_video_collector/requirements.txt +++ b/code/ch4_app/ch4_starter_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +18,22 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch5_partials/ch5_final_video_collector/models/video_model.py b/code/ch5_partials/ch5_final_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch5_partials/ch5_final_video_collector/models/video_model.py +++ b/code/ch5_partials/ch5_final_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.txt b/code/ch5_partials/ch5_final_video_collector/requirements.txt index 366e3db..f636e74 100644 --- a/code/ch5_partials/ch5_final_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_final_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch5_partials/ch5_starter_video_collector/models/video_model.py b/code/ch5_partials/ch5_starter_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch5_partials/ch5_starter_video_collector/models/video_model.py +++ b/code/ch5_partials/ch5_starter_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.txt b/code/ch5_partials/ch5_starter_video_collector/requirements.txt index 112b1af..71e7465 100644 --- a/code/ch5_partials/ch5_starter_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_starter_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -14,18 +18,22 @@ jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch6_active_search/ch6_final_video_collector/models/video_model.py b/code/ch6_active_search/ch6_final_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch6_active_search/ch6_final_video_collector/models/video_model.py +++ b/code/ch6_active_search/ch6_final_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.txt b/code/ch6_active_search/ch6_final_video_collector/requirements.txt index 366e3db..f636e74 100644 --- a/code/ch6_active_search/ch6_final_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_final_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch6_active_search/ch6_starter_video_collector/models/video_model.py b/code/ch6_active_search/ch6_starter_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/models/video_model.py +++ b/code/ch6_active_search/ch6_starter_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt index 366e3db..f636e74 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/models/video_model.py b/code/ch7_infinite_scroll/ch7_final_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/models/video_model.py +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt index 366e3db..f636e74 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/models/video_model.py b/code/ch7_infinite_scroll/ch7_starter_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/models/video_model.py +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt index 366e3db..f636e74 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt @@ -4,9 +4,13 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.2 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/code/starter_video_collector/models/video_model.py b/code/starter_video_collector/models/video_model.py index 4de5b4e..25908c0 100644 --- a/code/starter_video_collector/models/video_model.py +++ b/code/starter_video_collector/models/video_model.py @@ -9,4 +9,4 @@ class Video(BaseModel): url: str author: str views: int - category: Optional[str] + category: Optional[str] = None diff --git a/code/starter_video_collector/requirements.txt b/code/starter_video_collector/requirements.txt index 112b1af..f636e74 100644 --- a/code/starter_video_collector/requirements.txt +++ b/code/starter_video_collector/requirements.txt @@ -4,28 +4,39 @@ # # pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.3 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask +jinja-partials==0.1.1 + # via -r requirements.in jinja2==3.1.2 # via # -r requirements.in # flask -markupsafe==2.1.2 + # jinja-partials +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.5 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.5.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..6ec68a8 --- /dev/null +++ b/requirements.in @@ -0,0 +1,7 @@ +flask +Werkzeug +pydantic +more_itertools +MarkupSafe +Jinja2 +jinja-partials diff --git a/requirements.txt b/requirements.txt index 0763adc..f636e74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,15 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # -click==8.1.3 +annotated-types==0.5.0 + # via pydantic +blinker==1.6.2 + # via flask +click==8.1.7 # via flask -flask==2.2.2 +flask==2.3.3 # via -r requirements.in itsdangerous==2.1.2 # via flask @@ -17,18 +21,22 @@ jinja2==3.1.2 # -r requirements.in # flask # jinja-partials -markupsafe==2.1.1 +markupsafe==2.1.3 # via # -r requirements.in # jinja2 # werkzeug -more-itertools==9.0.0 +more-itertools==10.1.0 # via -r requirements.in -pydantic==1.10.2 +pydantic==2.3.0 # via -r requirements.in -typing-extensions==4.4.0 +pydantic-core==2.6.3 # via pydantic -werkzeug==2.2.3 +typing-extensions==4.7.1 + # via + # pydantic + # pydantic-core +werkzeug==2.3.7 # via # -r requirements.in # flask From 38c9f739b9cb503dcfed508406603a2fa6ca2e27 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 11 Mar 2024 16:26:23 -0700 Subject: [PATCH 10/18] Update the code to use the latest external packages. --- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 .../requirements.txt | 40 ++++++----------- ...{requirements.in => requirements.piptools} | 0 code/starter_video_collector/requirements.txt | 43 ++++++------------- requirements.in => requirements.piptools | 0 requirements.txt | 40 ++++++----------- 20 files changed, 140 insertions(+), 263 deletions(-) rename code/ch4_app/ch4_final_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch4_app/ch4_starter_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch5_partials/ch5_final_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch5_partials/ch5_starter_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch6_active_search/ch6_final_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch6_active_search/ch6_starter_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch7_infinite_scroll/ch7_final_video_collector/{requirements.in => requirements.piptools} (100%) rename code/ch7_infinite_scroll/ch7_starter_video_collector/{requirements.in => requirements.piptools} (100%) rename code/starter_video_collector/{requirements.in => requirements.piptools} (100%) rename requirements.in => requirements.piptools (100%) diff --git a/code/ch4_app/ch4_final_video_collector/requirements.in b/code/ch4_app/ch4_final_video_collector/requirements.piptools similarity index 100% rename from code/ch4_app/ch4_final_video_collector/requirements.in rename to code/ch4_app/ch4_final_video_collector/requirements.piptools diff --git a/code/ch4_app/ch4_final_video_collector/requirements.txt b/code/ch4_app/ch4_final_video_collector/requirements.txt index 71e7465..19fcb66 100644 --- a/code/ch4_app/ch4_final_video_collector/requirements.txt +++ b/code/ch4_app/ch4_final_video_collector/requirements.txt @@ -1,39 +1,27 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja2==3.1.2 - # via - # -r requirements.in - # flask -markupsafe==2.1.3 +jinja2==3.1.3 + # via flask +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.in b/code/ch4_app/ch4_starter_video_collector/requirements.piptools similarity index 100% rename from code/ch4_app/ch4_starter_video_collector/requirements.in rename to code/ch4_app/ch4_starter_video_collector/requirements.piptools diff --git a/code/ch4_app/ch4_starter_video_collector/requirements.txt b/code/ch4_app/ch4_starter_video_collector/requirements.txt index 71e7465..19fcb66 100644 --- a/code/ch4_app/ch4_starter_video_collector/requirements.txt +++ b/code/ch4_app/ch4_starter_video_collector/requirements.txt @@ -1,39 +1,27 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja2==3.1.2 - # via - # -r requirements.in - # flask -markupsafe==2.1.3 +jinja2==3.1.3 + # via flask +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.in b/code/ch5_partials/ch5_final_video_collector/requirements.piptools similarity index 100% rename from code/ch5_partials/ch5_final_video_collector/requirements.in rename to code/ch5_partials/ch5_final_video_collector/requirements.piptools diff --git a/code/ch5_partials/ch5_final_video_collector/requirements.txt b/code/ch5_partials/ch5_final_video_collector/requirements.txt index f636e74..959604b 100644 --- a/code/ch5_partials/ch5_final_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_final_video_collector/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.in b/code/ch5_partials/ch5_starter_video_collector/requirements.piptools similarity index 100% rename from code/ch5_partials/ch5_starter_video_collector/requirements.in rename to code/ch5_partials/ch5_starter_video_collector/requirements.piptools diff --git a/code/ch5_partials/ch5_starter_video_collector/requirements.txt b/code/ch5_partials/ch5_starter_video_collector/requirements.txt index 71e7465..19fcb66 100644 --- a/code/ch5_partials/ch5_starter_video_collector/requirements.txt +++ b/code/ch5_partials/ch5_starter_video_collector/requirements.txt @@ -1,39 +1,27 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja2==3.1.2 - # via - # -r requirements.in - # flask -markupsafe==2.1.3 +jinja2==3.1.3 + # via flask +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.in b/code/ch6_active_search/ch6_final_video_collector/requirements.piptools similarity index 100% rename from code/ch6_active_search/ch6_final_video_collector/requirements.in rename to code/ch6_active_search/ch6_final_video_collector/requirements.piptools diff --git a/code/ch6_active_search/ch6_final_video_collector/requirements.txt b/code/ch6_active_search/ch6_final_video_collector/requirements.txt index f636e74..959604b 100644 --- a/code/ch6_active_search/ch6_final_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_final_video_collector/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.in b/code/ch6_active_search/ch6_starter_video_collector/requirements.piptools similarity index 100% rename from code/ch6_active_search/ch6_starter_video_collector/requirements.in rename to code/ch6_active_search/ch6_starter_video_collector/requirements.piptools diff --git a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt index f636e74..959604b 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/requirements.txt +++ b/code/ch6_active_search/ch6_starter_video_collector/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.piptools similarity index 100% rename from code/ch7_infinite_scroll/ch7_final_video_collector/requirements.in rename to code/ch7_infinite_scroll/ch7_final_video_collector/requirements.piptools diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt index f636e74..959604b 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.piptools similarity index 100% rename from code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.in rename to code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.piptools diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt index f636e74..959604b 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/code/starter_video_collector/requirements.in b/code/starter_video_collector/requirements.piptools similarity index 100% rename from code/starter_video_collector/requirements.in rename to code/starter_video_collector/requirements.piptools diff --git a/code/starter_video_collector/requirements.txt b/code/starter_video_collector/requirements.txt index f636e74..19fcb66 100644 --- a/code/starter_video_collector/requirements.txt +++ b/code/starter_video_collector/requirements.txt @@ -1,42 +1,27 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 - # via - # -r requirements.in - # flask - # jinja-partials -markupsafe==2.1.3 +jinja2==3.1.3 + # via flask +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask diff --git a/requirements.in b/requirements.piptools similarity index 100% rename from requirements.in rename to requirements.piptools diff --git a/requirements.txt b/requirements.txt index f636e74..959604b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,42 +1,30 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -annotated-types==0.5.0 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +annotated-types==0.6.0 # via pydantic -blinker==1.6.2 +blinker==1.7.0 # via flask click==8.1.7 # via flask -flask==2.3.3 - # via -r requirements.in +flask==3.0.2 itsdangerous==2.1.2 # via flask -jinja-partials==0.1.1 - # via -r requirements.in -jinja2==3.1.2 +jinja-partials==0.2.0 +jinja2==3.1.3 # via - # -r requirements.in # flask # jinja-partials -markupsafe==2.1.3 +markupsafe==2.1.5 # via - # -r requirements.in # jinja2 # werkzeug -more-itertools==10.1.0 - # via -r requirements.in -pydantic==2.3.0 - # via -r requirements.in -pydantic-core==2.6.3 +more-itertools==10.2.0 +pydantic==2.6.3 +pydantic-core==2.16.3 # via pydantic -typing-extensions==4.7.1 +typing-extensions==4.10.0 # via # pydantic # pydantic-core -werkzeug==2.3.7 - # via - # -r requirements.in - # flask +werkzeug==3.0.1 + # via flask From 8eeb307dbd3aa191d32e835064ac9dc3592ece1c Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 11 Mar 2024 16:28:08 -0700 Subject: [PATCH 11/18] Update to the latest htmx, v1.9.10 --- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- .../starter_video_collector/static/js/htmx.js | 1564 +++++++++++++---- .../static/js/htmx.min.js | 5 +- 18 files changed, 10611 insertions(+), 3510 deletions(-) diff --git a/code/ch4_app/ch4_final_video_collector/static/js/htmx.js b/code/ch4_app/ch4_final_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch4_app/ch4_final_video_collector/static/js/htmx.js +++ b/code/ch4_app/ch4_final_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch4_app/ch4_final_video_collector/static/js/htmx.min.js b/code/ch4_app/ch4_final_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch4_app/ch4_final_video_collector/static/js/htmx.min.js +++ b/code/ch4_app/ch4_final_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch4_app/ch4_starter_video_collector/static/js/htmx.js b/code/ch4_app/ch4_starter_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch4_app/ch4_starter_video_collector/static/js/htmx.js +++ b/code/ch4_app/ch4_starter_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch4_app/ch4_starter_video_collector/static/js/htmx.min.js b/code/ch4_app/ch4_starter_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch4_app/ch4_starter_video_collector/static/js/htmx.min.js +++ b/code/ch4_app/ch4_starter_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch5_partials/ch5_final_video_collector/static/js/htmx.js b/code/ch5_partials/ch5_final_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch5_partials/ch5_final_video_collector/static/js/htmx.js +++ b/code/ch5_partials/ch5_final_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch5_partials/ch5_final_video_collector/static/js/htmx.min.js b/code/ch5_partials/ch5_final_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch5_partials/ch5_final_video_collector/static/js/htmx.min.js +++ b/code/ch5_partials/ch5_final_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.js b/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.js +++ b/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.min.js b/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.min.js +++ b/code/ch5_partials/ch5_starter_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.js b/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.js +++ b/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.min.js b/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.min.js +++ b/code/ch6_active_search/ch6_final_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.js b/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.js +++ b/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.min.js b/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.min.js +++ b/code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.js b/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.js +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.min.js b/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.min.js +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.js b/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.js +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.min.js b/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.min.js +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/code/starter_video_collector/static/js/htmx.js b/code/starter_video_collector/static/js/htmx.js index 27e57bc..86e7668 100644 --- a/code/starter_video_collector/static/js/htmx.js +++ b/code/starter_video_collector/static/js/htmx.js @@ -1,13 +1,23 @@ -//AMD insanity +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js +// + +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. (function (root, factory) { //@ts-ignore if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. //@ts-ignore define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); } else { // Browser globals - root.htmx = factory(); + root.htmx = root.htmx || factory(); } }(typeof self !== 'undefined' ? self : this, function () { return (function () { @@ -38,6 +48,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -53,15 +64,24 @@ return (function () { settlingClass:'htmx-settling', swappingClass:'htmx-swapping', allowEval:true, + allowScriptTags: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, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, }, parseInterval:parseInterval, _:internalEval, @@ -69,17 +89,23 @@ return (function () { return new EventSource(url, {withCredentials:true}) }, createWebSocket: function(url){ - return new WebSocket(url, []); + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; }, - version: "1.7.0" + version: "1.9.10" }; /** @type {import("./htmx").HtmxInternalApi} */ var internalAPI = { + addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -92,6 +118,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -105,21 +132,40 @@ return (function () { return "[hx-" + verb + "], [data-hx-" + verb + "]" }).join(", "); + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + //==================================================================== // Utilities //==================================================================== + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + function parseInterval(str) { if (str == undefined) { - return undefined + return undefined; } + + let interval = NaN; if (str.slice(-2) == "ms") { - return parseFloat(str.slice(0,-2)) || undefined - } - if (str.slice(-1) == "s") { - return (parseFloat(str.slice(0,-1)) * 1000) || undefined + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); } - return parseFloat(str) || undefined + return isNaN(interval) ? undefined : interval; } /** @@ -168,13 +214,11 @@ return (function () { * @returns {HTMLElement | null} */ function getClosestMatch(elt, condition) { - if (condition(elt)) { - return elt; - } else if (parentElt(elt)) { - return getClosestMatch(parentElt(elt), condition); - } else { - return null; + while (elt && !condition(elt)) { + elt = parentElt(elt); } + + return elt ? elt : null; } function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ @@ -208,7 +252,7 @@ return (function () { * @returns {boolean} */ function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatability + // @ts-ignore: non-standard properties for browser compatibility // noinspection JSUnresolvedVariable var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; return matchesFunction && matchesFunction.call(elt, selector); @@ -252,38 +296,47 @@ return (function () { return responseNode; } + function aFullPageResponse(resp) { + return /", 0); + function makeFragment(response) { + var partialResponse = !aFullPageResponse(response); + var startTag = getStartTag(response); + var content = response; + if (startTag === 'head') { + content = content.replace(HEAD_TAG_REGEX, ''); + } + if (htmx.config.useTemplateFragments && partialResponse) { + var documentFragment = parseHTML("", 0); // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? + // TODO: Are these close enough for htmx to use interchangeably? return documentFragment.querySelector('template').content; - } else { - var startTag = getStartTag(resp); - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + resp + "
", 1); - case "col": - return parseHTML("" + resp + "
", 2); - case "tr": - return parseHTML("" + resp + "
", 2); - case "td": - case "th": - return parseHTML("" + resp + "
", 3); - case "script": - return parseHTML("
" + resp + "
", 1); - default: - return parseHTML(resp, 0); - } + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("
" + content + "
", 1); + default: + return parseHTML(content, 0); } } @@ -365,13 +418,14 @@ return (function () { return elemTop < window.innerHeight && elemBottom >= 0; } - function bodyContains(elt) { - if (elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } function splitOnWhitespace(trigger) { return trigger.trim().split(/\s+/); @@ -402,6 +456,34 @@ return (function () { } } + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch(e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + //========================================================================================== // public API //========================================================================================== @@ -427,6 +509,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -446,7 +532,10 @@ return (function () { function removeElement(elt, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeElement(elt);}, delay) + setTimeout(function(){ + removeElement(elt); + elt = null; + }, delay); } else { elt.parentElement.removeChild(elt); } @@ -455,7 +544,10 @@ return (function () { function addClassToElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){addClassToElement(elt, clazz);}, delay) + setTimeout(function(){ + addClassToElement(elt, clazz); + elt = null; + }, delay); } else { elt.classList && elt.classList.add(clazz); } @@ -464,7 +556,10 @@ return (function () { function removeClassFromElement(elt, clazz, delay) { elt = resolveTarget(elt); if (delay) { - setTimeout(function(){removeClassFromElement(elt, clazz);}, delay) + setTimeout(function(){ + removeClassFromElement(elt, clazz); + elt = null; + }, delay); } else { if (elt.classList) { elt.classList.remove(clazz); @@ -494,26 +589,75 @@ return (function () { if (elt.closest) { return elt.closest(selector); } else { + // TODO remove when IE goes away do{ if (elt == null || matches(elt, selector)){ return elt; } } while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; } } function querySelectorAllExt(elt, selector) { if (selector.indexOf("closest ") === 0) { - return [closest(elt, selector.substr(8))]; + return [closest(elt, normalizeSelector(selector.substr(8)))]; } else if (selector.indexOf("find ") === 0) { - return [find(elt, selector.substr(5))]; + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; } else if (selector === 'document') { return [document]; } else if (selector === 'window') { return [window]; + } else if (selector === 'body') { + return [document.body]; } else { - return getDocument().querySelectorAll(selector); + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function(start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } } } @@ -570,7 +714,7 @@ return (function () { //==================================================================== // Node processing //==================================================================== - + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors function findAttributeTargets(elt, attrName) { var attrTarget = getClosestAttributeValue(elt, attrName); @@ -659,7 +803,7 @@ return (function () { * @returns */ function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; + var selector = "#" + getRawAttribute(oobElement, "id"); var swapStyle = "outerHTML"; if (oobValue === "true") { // do nothing @@ -703,7 +847,23 @@ return (function () { return oobValue; } - function handleOutOfBandSwaps(fragment, settleInfo) { + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); if (oobValue != null) { @@ -724,8 +884,11 @@ return (function () { function handleAttributes(parentNode, fragment, settleInfo) { forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var oldNode = parentNode.querySelector(newNode.tagName + "[id='" + newNode.id + "']"); + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); if (oldNode && oldNode !== parentNode) { var newAttributes = newNode.cloneNode(); cloneAttributes(newNode, oldNode); @@ -767,24 +930,67 @@ return (function () { } } - function cleanUpElement(element) { + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length){ + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if(attribute.value){ // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } if (internalData.webSocket) { internalData.webSocket.close(); } if (internalData.sseEventSource) { internalData.sseEventSource.close(); } - - triggerEvent(element, "htmx:beforeCleanupElement") - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function(info) { - if (element !== info.on) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { info.on.removeEventListener(info.trigger, info.listener); } }); } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function(key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); if (element.children) { // IE forEach(element.children, function(child) { cleanUpElement(child) }); } @@ -803,8 +1009,7 @@ return (function () { } else { newElt = eltBeforeNewContent.nextSibling; } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = [] // clear existing elements + settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target }); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -849,8 +1054,8 @@ return (function () { } } - function maybeSelectFromResponse(elt, fragment) { - var selector = getClosestAttributeValue(elt, "hx-select"); + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); if (selector) { var newFragment = getDocument().createDocumentFragment(); forEach(fragment.querySelectorAll(selector), function (node) { @@ -915,21 +1120,20 @@ return (function () { function findTitle(content) { if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); if (result) { return result[2]; } } } - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) { + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { settleInfo.title = findTitle(responseText); var fragment = makeFragment(responseText); if (fragment) { - handleOutOfBandSwaps(fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -949,7 +1153,10 @@ return (function () { } } } else { - triggerEvent(elt, triggerBody, []); + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } } } @@ -959,6 +1166,8 @@ return (function () { var SYMBOL_CONT = /[_$a-zA-Z0-9]/; var STRINGISH_START = ['"', "'", "/"]; var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; function tokenizeString(str) { var tokens = []; var position = 0; @@ -1041,101 +1250,137 @@ return (function () { function consumeUntil(tokens, match) { var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { + while (tokens.length > 0 && !match.test(tokens[0])) { result += tokens.shift(); } return result; } + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + var INPUT_SELECTOR = 'input, textarea, select'; /** * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs * @returns {import("./htmx").HtmxTriggerSpecification[]} */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + function parseAndCacheTrigger(elt, explicitTrigger, cache) { var triggerSpecs = []; - if (explicitTrigger) { - var tokens = tokenizeString(explicitTrigger); - do { - consumeUntil(tokens, NOT_WHITESPACE); - var initialLength = tokens.length; - var trigger = consumeUntil(tokens, /[,\[\s]/); - if (trigger !== "") { - if (trigger === "every") { - var every = {trigger: 'every'}; - consumeUntil(tokens, NOT_WHITESPACE); - every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); - consumeUntil(tokens, NOT_WHITESPACE); - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - every.eventFilter = eventFilter; - } - triggerSpecs.push(every); - } else if (trigger.indexOf("sse:") === 0) { - triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); - } else { - var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(elt, tokens, "event"); - if (eventFilter) { - triggerSpec.eventFilter = eventFilter; - } - while (tokens.length > 0 && tokens[0] !== ",") { - consumeUntil(tokens, NOT_WHITESPACE) - var token = tokens.shift(); - if (token === "changed") { - triggerSpec.changed = true; - } else if (token === "once") { - triggerSpec.once = true; - } else if (token === "consume") { - triggerSpec.consume = true; - } else if (token === "delay" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "from" && tokens[0] === ":") { - tokens.shift(); + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = {trigger: 'every'}; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); + } else { + var triggerSpec = {trigger: trigger}; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find") { + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } } - triggerSpec.from = from_arg; - } else if (token === "target" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.target = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if (token === "throttle" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); - } else if (token === "queue" && tokens[0] === ":") { - tokens.shift(); - triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else if ((token === "root" || token === "threshold") && tokens[0] === ":") { - tokens.shift(); - triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); - } else { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); } - triggerSpecs.push(triggerSpec); } + triggerSpecs.push(triggerSpec); } - if (tokens.length === initialLength) { - triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); - } - consumeUntil(tokens, NOT_WHITESPACE); - } while (tokens[0] === "," && tokens.shift()) + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()}); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) } if (triggerSpecs.length > 0) { return triggerSpecs; } else if (matches(elt, 'form')) { return [{trigger: 'submit'}]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')){ + return [{trigger: 'click'}]; } else if (matches(elt, INPUT_SELECTOR)) { return [{trigger: 'change'}]; } else { @@ -1147,14 +1392,17 @@ return (function () { getInternalData(elt).cancelled = true; } - function processPolling(elt, verb, path, spec) { + function processPolling(elt, handler, spec) { var nodeData = getInternalData(elt); nodeData.timeout = setTimeout(function () { if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, makeEvent('hx:poll:trigger', {triggerSpec:spec, target:elt}))) { - issueAjaxRequest(verb, path, elt); + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); } - processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), spec); + processPolling(elt, handler, spec); } }, spec.pollInterval); } @@ -1166,23 +1414,27 @@ return (function () { } function boostElement(elt, nodeData, triggerSpecs) { - if ((elt.tagName === "A" && isLocalLink(elt) && elt.target === "") || elt.tagName === "FORM") { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { nodeData.boosted = true; var verb, path; if (elt.tagName === "A") { verb = "get"; - path = getRawAttribute(elt, 'href'); - nodeData.pushURL = true; + path = getRawAttribute(elt, 'href') } else { var rawAttribute = getRawAttribute(elt, "method"); verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; if (verb === "get") { - nodeData.pushURL = true; } path = getRawAttribute(elt, 'action'); } triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, verb, path, nodeData, triggerSpec, true); + addEventListener(elt, function(elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); }); } } @@ -1213,11 +1465,11 @@ return (function () { return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); } - function maybeFilterEvent(triggerSpec, evt) { + function maybeFilterEvent(triggerSpec, elt, evt) { var eventFilter = triggerSpec.eventFilter; if(eventFilter){ try { - return eventFilter(evt) !== true; + return eventFilter.call(elt, evt) !== true; } catch(e) { triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); return true; @@ -1226,13 +1478,21 @@ return (function () { return false; } - function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) { + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); var eltsToListenOn; if (triggerSpec.from) { eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); } else { eltsToListenOn = [elt]; } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } forEach(eltsToListenOn, function (eltToListenOn) { var eventListener = function (evt) { if (!bodyContains(elt)) { @@ -1245,7 +1505,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1253,7 +1513,6 @@ return (function () { if (eventData.handledFor == null) { eventData.handledFor = []; } - var elementData = getInternalData(elt); if (eventData.handledFor.indexOf(elt) < 0) { eventData.handledFor.push(elt); if (triggerSpec.consume) { @@ -1272,11 +1531,11 @@ return (function () { } } if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { return; - } else { - elementData.lastValue = elt.value; } + eltToListenOnData.lastValue = eltToListenOn.value } if (elementData.delayed) { clearTimeout(elementData.delayed); @@ -1285,19 +1544,18 @@ return (function () { return; } - if (triggerSpec.throttle) { + if (triggerSpec.throttle > 0) { if (!elementData.throttle) { - issueAjaxRequest(verb, path, elt, evt); + handler(elt, evt); elementData.throttle = setTimeout(function () { elementData.throttle = null; }, triggerSpec.throttle); } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function () { - issueAjaxRequest(verb, path, elt, evt); - }, triggerSpec.delay); + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); } else { - issueAjaxRequest(verb, path, elt, evt); + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); } } }; @@ -1310,7 +1568,7 @@ return (function () { on: eltToListenOn }) eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }) + }); } var windowIsScrolling = false // used by initScrollHandler @@ -1336,14 +1594,11 @@ return (function () { if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { elt.setAttribute('data-hx-revealed', 'true'); var nodeData = getInternalData(elt); - if (nodeData.initialized) { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); } else { // if the node isn't initialized, wait for it before triggering the request - elt.addEventListener("htmx:afterProcessNode", - function () { - issueAjaxRequest(nodeData.verb, nodeData.path, elt); - }, {once: true}); + elt.addEventListener("htmx:afterProcessNode", function(evt) { triggerEvent(elt, 'revealed') }, {once: true}); } } } @@ -1502,6 +1757,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1518,7 +1776,7 @@ return (function () { var target = getTarget(elt) var settleInfo = makeSettleInfo(elt); - selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo) + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) settleImmediately(settleInfo.tasks) triggerEvent(elt, "htmx:sseMessage", event) }; @@ -1530,14 +1788,14 @@ return (function () { } } - function processSSETrigger(elt, verb, path, sseEventName) { + function processSSETrigger(elt, handler, sseEventName) { var sseSourceElt = getClosestMatch(elt, hasEventSource); if (sseSourceElt) { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function () { if (!maybeCloseSSESource(sseSourceElt)) { if (bodyContains(elt)) { - issueAjaxRequest(verb, path, elt); + handler(elt); } else { sseEventSource.removeEventListener(sseEventName, sseListener); } @@ -1563,14 +1821,14 @@ return (function () { //==================================================================== - function loadImmediately(elt, verb, path, nodeData, delay) { + function loadImmediately(elt, handler, nodeData, delay) { var load = function(){ if (!nodeData.loaded) { nodeData.loaded = true; - issueAjaxRequest(verb, path, elt); + handler(elt); } } - if (delay) { + if (delay > 0) { setTimeout(load, delay); } else { load(); @@ -1586,46 +1844,59 @@ return (function () { nodeData.path = path; nodeData.verb = verb; triggerSpecs.forEach(function(triggerSpec) { - if (triggerSpec.sseEvent) { - processSSETrigger(elt, verb, path, triggerSpec.sseEvent); - } else if (triggerSpec.trigger === "revealed") { - initScrollHandler(); - maybeReveal(elt); - } else if (triggerSpec.trigger === "intersect") { - var observerOptions = {}; - if (triggerSpec.root) { - observerOptions.root = querySelectorExt(elt, triggerSpec.root) - } - if (triggerSpec.threshold) { - observerOptions.threshold = parseFloat(triggerSpec.threshold); + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return } - var observer = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - var entry = entries[i]; - if (entry.isIntersecting) { - triggerEvent(elt, "intersect"); - break; - } - } - }, observerOptions); - observer.observe(elt); - addEventListener(elt, verb, path, nodeData, triggerSpec); - } else if (triggerSpec.trigger === "load") { - loadImmediately(elt, verb, path, nodeData, triggerSpec.delay); - } else if (triggerSpec.pollInterval) { - nodeData.polling = true; - processPolling(elt, verb, path, triggerSpec); - } else { - addEventListener(elt, verb, path, nodeData, triggerSpec); - } + issueAjaxRequest(verb, path, elt, evt) + }) }); } }); return explicitAction; } + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + function evalScript(script) { - if (script.type === "text/javascript" || script.type === "module" || script.type === "") { + if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) { var newScript = getDocument().createElement("script"); forEach(script.attributes, function (attr) { newScript.setAttribute(attr.name, attr.value); @@ -1642,7 +1913,10 @@ return (function () { } catch (e) { logError(e); } finally { - parent.removeChild(script); + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } } } } @@ -1656,48 +1930,187 @@ return (function () { }); } - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements } function findElementsToProcess(elt) { if (elt.querySelectorAll) { - var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; - var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," + - " [data-hx-ws], [hx-ext], [hx-data-ext]"); + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return results; } else { return []; } } - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - if (matches(evt.target, "button, input[type='submit']")) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = evt.target; - } - }; - + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt){ + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { // need to handle both click and focus in: // focusin - in case someone tabs in to a button and hits the space bar // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } - form.addEventListener('click', maybeSetLastButtonClicked) - form.addEventListener('focusin', maybeSetLastButtonClicked) - form.addEventListener('focusout', function(evt){ - var internalData = getInternalData(form); - internalData.lastButtonClicked = null; - }) + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function() { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({event:eventName, listener:listener}); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } } function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) return; } var nodeData = getInternalData(elt); - if (!nodeData.initialized) { - nodeData.initialized = true; + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + triggerEvent(elt, "htmx:beforeProcessNode") if (elt.value) { @@ -1705,14 +2118,24 @@ return (function () { } var triggerSpecs = getTriggerSpecs(elt); - var explicitAction = processVerbs(elt, nodeData, triggerSpecs); - - if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") { - boostElement(elt, nodeData, triggerSpecs); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } } - if (elt.tagName === "FORM") { - initButtonTracking(elt); + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) } var sseInfo = getAttributeValue(elt, 'hx-sse'); @@ -1730,8 +2153,15 @@ return (function () { function processNode(elt) { elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } initNode(elt); forEach(findElementsToProcess(elt), function(child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); } //==================================================================== @@ -1809,7 +2239,7 @@ return (function () { eventResult = eventResult && elt.dispatchEvent(kebabedEvent) } withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) }); return eventResult; } @@ -1825,6 +2255,18 @@ return (function () { } function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1832,7 +2274,9 @@ return (function () { break; } } - historyCache.push({url:url, content: content, title:title, scroll:scroll}) + var newHistoryItem = {url:url, content: content, title:title, scroll:scroll}; + triggerEvent(getDocument().body, "htmx:historyItemCreated", {item:newHistoryItem, cache: historyCache}) + historyCache.push(newHistoryItem) while (historyCache.length > htmx.config.historyCacheSize) { historyCache.shift(); } @@ -1848,6 +2292,12 @@ return (function () { } function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; for (var i = 0; i < historyCache.length; i++) { if (historyCache[i].url === url) { @@ -1869,13 +2319,43 @@ return (function () { function saveCurrentPageToHistory() { var elt = getHistoryElement(); var path = currentPathForHistory || location.pathname+location.search; - triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path:path, historyElt:elt}); - if(htmx.config.historyEnabled) history.replaceState({htmx:true}, getDocument().title, window.location.href); - saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt}); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({htmx: true}, getDocument().title, window.location.href); } function pushUrlIntoHistory(path) { - if(htmx.config.historyEnabled) history.pushState({htmx:true}, "", path); + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if(htmx.config.historyEnabled) { + history.pushState({htmx:true}, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if(htmx.config.historyEnabled) history.replaceState({htmx:true}, "", path); currentPathForHistory = path; } @@ -1890,7 +2370,9 @@ return (function () { var details = {path: path, xhr:request}; triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); request.onload = function () { if (this.status >= 200 && this.status < 400) { triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); @@ -1899,11 +2381,20 @@ return (function () { fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; var historyElement = getHistoryElement(); var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } // @ts-ignore swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path: path, cacheMiss:true, serverResponse:this.response}); } else { triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); } @@ -1922,9 +2413,11 @@ return (function () { swapInnerHTML(historyElement, fragment, settleInfo) settleImmediately(settleInfo.tasks); document.title = cached.title; - window.scrollTo(0, cached.scroll); + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout currentPathForHistory = path; - triggerEvent(getDocument().body, "htmx:historyRestore", {path:path}); + triggerEvent(getDocument().body, "htmx:historyRestore", {path:path, item:cached}); } else { if (htmx.config.refreshOnHistoryMiss) { @@ -1936,31 +2429,46 @@ return (function () { } } - function shouldPush(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl && pushUrl !== "false") || - (getInternalData(elt).boosted && getInternalData(elt).pushURL); - } - - function getPushUrl(elt) { - var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); - return (pushUrl === "true" || pushUrl === "false") ? null : pushUrl; - } - function addRequestIndicatorClasses(elt) { var indicators = findAttributeTargets(elt, 'hx-indicator'); if (indicators == null) { indicators = [elt]; } forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; ic.classList["add"].call(ic.classList, htmx.config.requestClass); }); return indicators; } - function removeRequestIndicatorClasses(indicators) { + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { forEach(indicators, function (ic) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } }); } @@ -1979,7 +2487,7 @@ return (function () { } function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { + if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { return false; } // ignore "submitter" types (see jQuery src/serialize.js) @@ -1992,6 +2500,29 @@ return (function () { return true; } + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + function processInputValue(processed, values, errors, elt, validate) { if (elt == null || haveSeenNode(processed, elt)) { return; @@ -2001,35 +2532,14 @@ return (function () { if (shouldInclude(elt)) { var name = getRawAttribute(elt,"name"); var value = elt.value; - if (elt.multiple) { + if (elt.multiple && elt.tagName === "SELECT") { value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); } // include file inputs if (elt.files) { value = toArray(elt.files); } - // This is a little ugly because both the current value of the named value in the form - // and the new value could be arrays, so we have to handle all four cases :/ - if (name != null && value != null) { - var current = values[name]; - if(current) { - if (Array.isArray(current)) { - if (Array.isArray(value)) { - values[name] = current.concat(value); - } else { - current.push(value); - } - } else { - if (Array.isArray(value)) { - values[name] = [current].concat(value); - } else { - values[name] = [current, value]; - } - } - } else { - values[name] = value; - } - } + addValueToValues(name, value, values); if (validate) { validateElement(elt, errors); } @@ -2062,9 +2572,13 @@ return (function () { var formValues = {}; var errors = []; var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } // only validate when form is directly submitted and novalidate or formnovalidate are not set - var validate = matches(elt, 'form') && elt.noValidate !== true; + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; if (internalData.lastButtonClicked) { validate = validate && internalData.lastButtonClicked.formNoValidate !== true; } @@ -2078,11 +2592,11 @@ return (function () { processInputValue(processed, values, errors, elt, validate); // if a button or submit was clicked last, include its value - if (internalData.lastButtonClicked) { - var name = getRawAttribute(internalData.lastButtonClicked,"name"); - if (name) { - values[name] = internalData.lastButtonClicked.value; - } + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) } // include any explicit includes @@ -2228,40 +2742,43 @@ return (function () { "swapDelay" : htmx.config.defaultSwapDelay, "settleDelay" : htmx.config.defaultSettleDelay } - if (getInternalData(elt).boosted && !isAnchorLink(elt)) { + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { swapSpec["show"] = "top" } if (swapInfo) { var split = splitOnWhitespace(swapInfo); if (split.length > 0) { - swapSpec["swapStyle"] = split[0]; - for (var i = 1; i < split.length; i++) { - var modifier = split[i]; - if (modifier.indexOf("swap:") === 0) { - swapSpec["swapDelay"] = parseInterval(modifier.substr(5)); - } - if (modifier.indexOf("settle:") === 0) { - swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.substr(7); + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); var splitSpec = scrollSpec.split(":"); var scrollVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["scroll"] = scrollVal; swapSpec["scrollTarget"] = selectorVal; - } - if (modifier.indexOf("show:") === 0) { - var showSpec = modifier.substr(5); + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); var splitSpec = showSpec.split(":"); var showVal = splitSpec.pop(); var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; swapSpec["show"] = showVal; swapSpec["showTarget"] = selectorVal; - } - if (modifier.indexOf("focus-scroll:") === 0) { - var focusScrollVal = modifier.substr("focus-scroll:".length); + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); } } } @@ -2269,6 +2786,11 @@ return (function () { return swapSpec; } + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + function encodeParamsForBody(xhr, elt, filteredParameters) { var encodedParameters = null; withExtensions(elt, function (extension) { @@ -2279,8 +2801,7 @@ return (function () { if (encodedParameters != null) { return encodedParameters; } else { - if (getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || - (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data")) { + if (usesFormData(elt)) { return makeFormData(filteredParameters); } else { return urlEncode(filteredParameters); @@ -2352,6 +2873,9 @@ return (function () { if (attributeValue) { var str = attributeValue.trim(); var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } if (str.indexOf("javascript:") === 0) { str = str.substr(11); evaluateValue = true; @@ -2426,7 +2950,7 @@ return (function () { } } - function getResponseURL(xhr) { + function getPathFromResponse(xhr) { // NB: IE11 does not support this stuff if (xhr.responseURL && typeof(URL) !== "undefined") { try { @@ -2439,7 +2963,7 @@ return (function () { } function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); + return regexp.test(xhr.getAllResponseHeaders()) } function ajaxHelper(verb, path, context) { @@ -2458,6 +2982,7 @@ return (function () { values : context.values, targetOverride: resolveTarget(context.target), swapOverride: context.swap, + select: context.select, returnPromise: true }); } @@ -2477,7 +3002,28 @@ return (function () { return arr; } - function issueAjaxRequest(verb, path, elt, event, etc) { + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { var resolve = null; var reject = null; etc = etc != null ? etc : {}; @@ -2491,18 +3037,52 @@ return (function () { elt = getDocument().body; } var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; } var target = etc.targetOverride || getTarget(elt); if (target == null || target == DUMMY_ELT) { triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; + maybeCall(reject); + return promise; } - var syncElt = elt; var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function(skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion}; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); var queueStrategy = null; var abortable = false; @@ -2518,10 +3098,12 @@ return (function () { syncStrategy = (syncStrings[1] || 'drop').trim(); eltData = getInternalData(syncElt); if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; + maybeCall(resolve); + return promise; } else if (syncStrategy === "abort") { if (eltData.xhr) { - return; + maybeCall(resolve); + return promise; } else { abortable = true; } @@ -2565,7 +3147,8 @@ return (function () { issueAjaxRequest(verb, path, elt, event, etc) }); } - return; + maybeCall(resolve); + return promise; } } @@ -2593,8 +3176,7 @@ return (function () { } } - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { + if (confirmQuestion && !confirmed) { if(!confirm(confirmQuestion)) { maybeCall(resolve); endRequestLock() @@ -2604,6 +3186,11 @@ return (function () { var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + if (etc.headers) { headers = mergeObjects(headers, etc.headers); } @@ -2617,8 +3204,8 @@ return (function () { var allParameters = mergeObjects(rawParameters, expressionVars); var filteredParameters = filterValues(allParameters, elt); - if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; } // behavior of anchors w/ empty href is to use the current URL @@ -2626,9 +3213,16 @@ return (function () { path = getDocument().location.href; } + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, parameters: filteredParameters, unfilteredParameters: allParameters, headers:headers, @@ -2653,6 +3247,7 @@ return (function () { headers = requestConfig.headers; filteredParameters = requestConfig.parameters; errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; if(errors && errors.length > 0){ triggerEvent(elt, 'htmx:validation:halted', requestConfig) @@ -2664,25 +3259,31 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - if (verb === 'get') { - var finalPathForGet = pathNoAnchor; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; var values = Object.keys(filteredParameters).length !== 0; if (values) { - if (finalPathForGet.indexOf("?") < 0) { - finalPathForGet += "?"; + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; } else { - finalPathForGet += "&"; + finalPath += "&"; } - finalPathForGet += urlEncode(filteredParameters); + finalPath += urlEncode(filteredParameters); if (anchor) { - finalPathForGet += "#" + anchor; + finalPath += "#" + anchor; } } - xhr.open('GET', finalPathForGet, true); - } else { - xhr.open(verb.toUpperCase(), path, true); } + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -2699,19 +3300,24 @@ return (function () { } } - var responseInfo = {xhr: xhr, target: target, requestConfig: requestConfig, etc:etc, pathInfo:{ - path:path, finalPath:finalPathForGet, anchor:anchor + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor } }; xhr.onload = function () { try { var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); responseHandler(elt, responseInfo); - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerEvent(elt, 'htmx:afterRequest', responseInfo); triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // if the body no longer contains the element, trigger the even on the closest parent + // if the body no longer contains the element, trigger the event on the closest parent // remaining in the DOM if (!bodyContains(elt)) { var secondaryTriggerElt = null; @@ -2734,21 +3340,21 @@ return (function () { } } xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendError', responseInfo); maybeCall(reject); endRequestLock(); } xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); maybeCall(reject); endRequestLock(); } xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); + removeRequestIndicators(indicators, disableElts); triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); triggerErrorEvent(elt, 'htmx:timeout', responseInfo); maybeCall(reject); @@ -2760,6 +3366,7 @@ return (function () { return promise } var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { forEach([xhr, xhr.upload], function (target) { @@ -2773,14 +3380,99 @@ return (function () { }); }); triggerEvent(elt, 'htmx:beforeSend', responseInfo); - xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters)); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); return promise; } + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr,/HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr,/HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path : pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type:saveType, + path: path + } + } else { + return {}; + } + } + function handleAjaxResponse(elt, responseInfo) { var xhr = responseInfo.xhr; var target = responseInfo.target; var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; @@ -2788,33 +3480,44 @@ return (function () { handleTrigger(xhr, "HX-Trigger", elt); } - if (hasHeader(xhr,/HX-Push:/i)) { - var pushedUrl = xhr.getResponseHeader("HX-Push"); + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function(){ + pushUrlIntoHistory(redirectPath); + }); + return; } + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + if (hasHeader(xhr, /HX-Redirect:/i)) { - window.location.href = xhr.getResponseHeader("HX-Redirect"); + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); return; } - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } + if (shouldRefresh) { + location.reload(); + return; } if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } } - /** @type {boolean} */ - var shouldSaveHistory - if (pushedUrl == "false") { - shouldSaveHistory = false - } else { - shouldSaveHistory = shouldPush(elt) || pushedUrl; - } + var historyUpdate = determineHistoryUpdates(elt, responseInfo); // by default htmx only swaps on 200 return codes and does not swap // on 204 'No Content' @@ -2823,15 +3526,18 @@ return (function () { var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; var serverResponse = xhr.response; var isError = xhr.status >= 400; - var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo); + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo); if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; target = beforeSwapDetails.target; // allow re-targeting serverResponse = beforeSwapDetails.serverResponse; // allow updating content isError = beforeSwapDetails.isError; // allow updating error - + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events responseInfo.failed = isError; // Make failed property available to response events - responseInfo.successful = !isError; // Make successful property available to response events + responseInfo.successful = !isError; // Make successful property available to response events if (beforeSwapDetails.shouldSwap) { if (xhr.status === 286) { @@ -2842,18 +3548,29 @@ return (function () { serverResponse = extension.transformResponse(serverResponse, xhr, elt); }); - // Save current page - if (shouldSaveHistory) { + // Save current page if there will be a history update + if (historyUpdate.type) { saveCurrentPageToHistory(); } var swapOverride = etc.swapOverride; + if (hasHeader(xhr,/HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } var swapSpec = getSwapSpecification(elt, swapOverride); + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -2868,19 +3585,44 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path}); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path}); + } + } + var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); if (selectionInfo.elt && !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(selectionInfo.elt.id); + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; if (newActiveElt) { // @ts-ignore if (selectionInfo.start && newActiveElt.setSelectionRange) { // @ts-ignore - newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } } newActiveElt.focus(focusOptions); } @@ -2893,9 +3635,6 @@ return (function () { } triggerEvent(elt, 'htmx:afterSwap', responseInfo); }); - if (responseInfo.pathInfo.anchor) { - location.hash = responseInfo.pathInfo.anchor; - } if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { var finalElt = elt; @@ -2915,14 +3654,15 @@ return (function () { } triggerEvent(elt, 'htmx:afterSettle', responseInfo); }); - // push URL and save new page - if (shouldSaveHistory) { - var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || responseInfo.pathInfo.finalPath || responseInfo.pathInfo.path; - pushUrlIntoHistory(pathToPush); - triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: pathToPush}); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if(anchorTarget) { + anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); + } } - if(settleInfo.title) { + if(settleInfo.title && !ignoreTitle) { var titleElt = find("title"); if(titleElt) { titleElt.innerHTML = settleInfo.title; @@ -2940,6 +3680,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -2949,10 +3690,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { @@ -2960,7 +3725,7 @@ return (function () { } } if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.path}, responseInfo)); + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); } } @@ -3048,9 +3813,22 @@ return (function () { //==================================================================== // Initialization //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function() { + isReady = true + }) + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ function ready(fn) { - if (getDocument().readyState !== 'loading') { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { fn(); } else { getDocument().addEventListener('DOMContentLoaded', fn); @@ -3061,9 +3839,9 @@ return (function () { if (htmx.config.includeIndicatorStyles !== false) { getDocument().head.insertAdjacentHTML("beforeend", ""); } } @@ -3101,6 +3879,9 @@ return (function () { internalData.xhr.abort(); } }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ window.onpopstate = function (event) { if (event.state && event.state.htmx) { restoreHistory(); @@ -3110,10 +3891,15 @@ return (function () { 'triggerEvent': triggerEvent }); }); + } else { + if (originalPopstate) { + originalPopstate(event); + } } }; setTimeout(function () { triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc }, 0); }) diff --git a/code/starter_video_collector/static/js/htmx.min.js b/code/starter_video_collector/static/js/htmx.min.js index 998414c..53bbdf6 100644 --- a/code/starter_video_collector/static/js/htmx.min.js +++ b/code/starter_video_collector/static/js/htmx.min.js @@ -1 +1,4 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var U={onLoad:t,process:ct,on:M,off:D,trigger:$,ajax:er,find:C,findAll:R,closest:H,values:function(e,t){var r=Mt(e,t||"post");return r.values},remove:O,addClass:L,removeClass:q,toggleClass:A,takeClass:T,defineExtension:or,removeExtension:ar,logAll:E,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",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])},version:"1.7.0"};var r={bodyContains:Y,filterValues:jt,hasAttribute:s,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Gt,getHeaders:Xt,getInputValues:Mt,getInternalData:_,getSwapSpecification:Ut,getTriggerSpecs:ke,getTarget:ne,makeFragment:g,mergeObjects:Q,makeSettleInfo:zt,oobSwap:B,selectAndSwap:we,settleImmediately:Ct,shouldCancel:Pe,triggerEvent:$,triggerErrorEvent:J,withExtensions:gt};var n=["get","post","put","delete","patch"];var i=n.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}return parseFloat(e)||undefined}function f(e,t){return e.getAttribute&&e.getAttribute(t)}function s(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function V(e,t){return f(e,t)||f(e,"data-"+t)}function u(e){return e.parentElement}function z(){return document}function h(e,t){if(t(e)){return e}else if(u(e)){return h(u(e),t)}else{return null}}function o(e,t,r){var n=V(t,r);var i=V(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function G(t,r){var n=null;h(t,function(e){return n=o(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function a(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 l(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=z().createDocumentFragment()}return i}function g(e){if(U.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=a(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function K(e){if(e){e()}}function p(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function m(e){return p(e,"Function")}function x(e){return p(e,"Object")}function _(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function Y(e){if(e.getRootNode()instanceof ShadowRoot){return z().body.contains(e.getRootNode().host)}else{return z().body.contains(e)}}function w(e){return e.trim().split(/\s+/)}function Q(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){pt(e);return null}}function e(e){return Jt(z().body,function(){return eval(e)})}function t(t){var e=U.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){U.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C(z(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R(z(),e)}}function O(e,t){e=k(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function L(e,t,r){e=k(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList&&e.classList.add(t)}}function q(e,t,r){e=k(e);if(r){setTimeout(function(){q(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function A(e,t){e=k(e);e.classList.toggle(t)}function T(e,t){e=k(e);W(e.parentElement.children,function(e){q(e,t)});L(e,t)}function H(e,t){e=k(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e))}}function N(e,t){if(t.indexOf("closest ")===0){return[H(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return z().querySelectorAll(t)}}function ee(e,t){if(t){return N(e,t)[0]}else{return N(z().body,e)[0]}}function k(e){if(p(e,"String")){return C(e)}else{return e}}function I(e,t,r){if(m(t)){return{target:z().body,event:e,listener:t}}else{return{target:k(e),event:t,listener:r}}}function M(t,r,n){lr(function(){var e=I(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=m(r);return e?r:n}function D(t,r,n){lr(function(){var e=I(t,r,n);e.target.removeEventListener(e.event,e.listener)});return m(r)?r:n}var te=z().createElement("output");function F(e,t){var r=G(e,t);if(r){if(r==="this"){return[re(e,t)]}else{var n=N(e,r);if(n.length===0){pt('The selector "'+r+'" on '+t+" returned no matches!");return[te]}else{return n}}}}function re(e,t){return h(e,function(e){return V(e,t)!=null})}function ne(e){var t=G(e,"hx-target");if(t){if(t==="this"){return re(e,"hx-target")}else{return ee(e,t)}}else{var r=_(e);if(r.boosted){return z().body}else{return e}}}function P(e){var t=U.config.attributesToSettle;for(var r=0;r0){a=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{a=e}var r=z().querySelectorAll(t);if(r){W(r,function(e){var t;var r=i.cloneNode(true);t=z().createDocumentFragment();t.appendChild(r);if(!j(a,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!$(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){ye(a,e,e,t,o)}W(o.elts,function(e){$(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);J(z().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ie(e,r){W(R(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=V(e,"hx-swap-oob");if(t!=null){B(t,e,r)}})}function oe(e){W(R(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=V(e,"id");var r=z().getElementById(t);if(r!=null){e.parentNode.replaceChild(r,e)}})}function ae(n,e,i){W(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();X(e,t);i.tasks.push(function(){X(e,r)})}}})}function se(e){return function(){q(e,U.config.addedClass);ct(e);at(e);le(e);$(e,"htmx:load")}}function le(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ue(e,t,r,n){ae(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;L(i,U.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(se(i))}}}function fe(t){var e=_(t);if(e.webSocket){e.webSocket.close()}if(e.sseEventSource){e.sseEventSource.close()}$(t,"htmx:beforeCleanupElement");if(e.listenerInfos){W(e.listenerInfos,function(e){if(t!==e.on){e.on.removeEventListener(e.trigger,e.listener)}})}if(t.children){W(t.children,function(e){fe(e)})}}function ce(e,t,r){if(e.tagName==="BODY"){return me(e,t,r)}else{var n;var i=e.previousSibling;ue(u(e),e,t,r);if(i==null){n=u(e).firstChild}else{n=i.nextSibling}_(e).replacedWith=n;r.elts=[];while(n&&n!==e){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}fe(e);u(e).removeChild(e)}}function he(e,t,r){return ue(e,e.firstChild,t,r)}function de(e,t,r){return ue(u(e),e,t,r)}function ve(e,t,r){return ue(e,null,t,r)}function ge(e,t,r){return ue(u(e),e.nextSibling,t,r)}function pe(e,t,r){fe(e);return u(e).removeChild(e)}function me(e,t,r){var n=e.firstChild;ue(e,n,t,r);if(n){while(n.nextSibling){fe(n.nextSibling);e.removeChild(n.nextSibling)}fe(n);e.removeChild(n)}}function xe(e,t){var r=G(e,"hx-select");if(r){var n=z().createDocumentFragment();W(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function ye(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":ce(r,n,i);return;case"afterbegin":he(r,n,i);return;case"beforebegin":de(r,n,i);return;case"beforeend":ve(r,n,i);return;case"afterend":ge(r,n,i);return;case"delete":pe(r,n,i);return;default:var o=sr(t);for(var a=0;a-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function we(e,t,r,n,i){i.title=be(n);var o=g(n);if(o){ie(o,i);o=xe(r,o);oe(o);return ye(e,r,t,o,i)}}function Se(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var o in i){if(i.hasOwnProperty(o)){var a=i[o];if(!x(a)){a={value:a}}$(r,o,a)}}}else{$(r,n,[])}}var Ee=/\s/;var Ce=/[\s,]/;var Re=/[_$a-zA-Z]/;var Oe=/[_$a-zA-Z0-9]/;var Le=['"',"'","/"];var qe=/[^\s]/;function Ae(e){var t=[];var r=0;while(r0){var a=t[0];if(a==="]"){n--;if(n===0){if(o===null){i=i+"true"}t.shift();i+=")})";try{var s=Jt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){J(z().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(a==="["){n++}if(Te(a,o,r)){i+="(("+r+"."+a+") ? ("+r+"."+a+") : (window."+a+"))"}else{i=i+a}o=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Ne="input, textarea, select";function ke(e){var t=V(e,"hx-trigger");var r=[];if(t){var n=Ae(t);do{c(n,qe);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var o={trigger:"every"};c(n,qe);o.pollInterval=v(c(n,/[,\[\s]/));c(n,qe);var a=He(e,n,"event");if(a){o.eventFilter=a}r.push(o)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var a=He(e,n,"event");if(a){s.eventFilter=a}while(n.length>0&&n[0]!==","){c(n,qe);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Ce))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Ce);if(u==="closest"||u==="find"){n.shift();u+=" "+c(n,Ce)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Ce)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Ce))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Ce)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Ce)}else{J(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){J(e,"htmx:syntax:error",{token:n.shift()})}c(n,qe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,Ne)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ie(e){_(e).cancelled=true}function Me(e,t,r,n){var i=_(e);i.timeout=setTimeout(function(){if(Y(e)&&i.cancelled!==true){if(!je(n,dt("hx:poll:trigger",{triggerSpec:n,target:e}))){Z(t,r,e)}Me(e,t,V(e,"hx-"+t),n)}},n.pollInterval)}function De(e){return location.hostname===e.hostname&&f(e,"href")&&f(e,"href").indexOf("#")!==0}function Fe(t,r,e){if(t.tagName==="A"&&De(t)&&t.target===""||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=f(t,"href");r.pushURL=true}else{var o=f(t,"method");n=o?o.toLowerCase():"get";if(n==="get"){r.pushURL=true}i=f(t,"action")}e.forEach(function(e){Be(t,n,i,r,e,true)})}}function Pe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&H(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function Xe(e,t){return _(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function je(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){J(z().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Be(o,a,s,e,l,u){var t;if(l.from){t=N(o,l.from)}else{t=[o]}W(t,function(n){var i=function(e){if(!Y(o)){n.removeEventListener(l.trigger,i);return}if(Xe(o,e)){return}if(u||Pe(e,o)){e.preventDefault()}if(je(l,e)){return}var t=_(e);t.triggerSpec=l;if(t.handledFor==null){t.handledFor=[]}var r=_(o);if(t.handledFor.indexOf(o)<0){t.handledFor.push(o);if(l.consume){e.stopPropagation()}if(l.target&&e.target){if(!d(e.target,l.target)){return}}if(l.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(l.changed){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(l.throttle){if(!r.throttle){Z(a,s,o,e);r.throttle=setTimeout(function(){r.throttle=null},l.throttle)}}else if(l.delay){r.delayed=setTimeout(function(){Z(a,s,o,e)},l.delay)}else{Z(a,s,o,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:l.trigger,listener:i,on:n});n.addEventListener(l.trigger,i)})}var Ue=false;var Ve=null;function ze(){if(!Ve){Ve=function(){Ue=true};window.addEventListener("scroll",Ve);setInterval(function(){if(Ue){Ue=false;W(z().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){_e(e)})}},200)}}function _e(e){if(!s(e,"data-hx-revealed")&&b(e)){e.setAttribute("data-hx-revealed","true");var t=_(e);if(t.initialized){Z(t.verb,t.path,e)}else{e.addEventListener("htmx:afterProcessNode",function(){Z(t.verb,t.path,e)},{once:true})}}}function We(e,t,r){var n=w(r);for(var i=0;i=0){var t=Ge(n);setTimeout(function(){Je(s,r,n+1)},t)}};t.onopen=function(e){n=0};_(s).webSocket=t;t.addEventListener("message",function(e){if($e(s)){return}var t=e.data;gt(s,function(e){t=e.transformResponse(t,null,s)});var r=zt(s);var n=g(t);var i=y(n.children);for(var o=0;o0){$(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Pe(e,u)){e.preventDefault()}})}else{J(u,"htmx:noWebSocketSourceError")}}function Ge(e){var t=U.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}pt('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function Ke(e,t,r){var n=w(r);for(var i=0;iU.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){J(z().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function bt(e){var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){$(z().body,"htmx:historyCacheMissLoad",i);var e=g(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=xt();var r=zt(t);me(t,e,r);Ct(r.tasks);mt=n;$(z().body,"htmx:historyRestore",{path:n})}else{J(z().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Ot(e){St();e=e||location.pathname+location.search;var t=bt(e);if(t){var r=g(t.content);var n=xt();var i=zt(n);me(n,r,i);Ct(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);mt=e;$(z().body,"htmx:historyRestore",{path:e})}else{if(U.config.refreshOnHistoryMiss){window.location.reload(true)}else{Rt(e)}}}function Lt(e){var t=G(e,"hx-push-url");return t&&t!=="false"||_(e).boosted&&_(e).pushURL}function qt(e){var t=G(e,"hx-push-url");return t==="true"||t==="false"?null:t}function At(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}W(t,function(e){e.classList["add"].call(e.classList,U.config.requestClass)});return t}function Tt(e){W(e,function(e){e.classList["remove"].call(e.classList,U.config.requestClass)})}function Ht(e,t){for(var r=0;r=0}function Ut(e,t){var r=t?t:G(e,"hx-swap");var n={swapStyle:_(e).boosted?"innerHTML":U.config.defaultSwapStyle,swapDelay:U.config.defaultSwapDelay,settleDelay:U.config.defaultSettleDelay};if(_(e).boosted&&!Bt(e)){n["show"]="top"}if(r){var i=w(r);if(i.length>0){n["swapStyle"]=i[0];for(var o=1;o0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(a.indexOf("show:")===0){var c=a.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(a.indexOf("focus-scroll:")===0){var d=a.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Vt(t,r,n){var i=null;gt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(G(r,"hx-encoding")==="multipart/form-data"||d(r,"form")&&f(r,"enctype")==="multipart/form-data"){return Pt(n)}else{return Ft(n)}}}function zt(e){return{tasks:[],elts:[e]}}function _t(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ee(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var o=t.showTarget;if(t.showTarget==="window"){o="body"}i=ee(r,o)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:U.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:U.config.scrollBehavior})}}}function Wt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=V(e,t);if(i){var o=i.trim();var a=r;if(o.indexOf("javascript:")===0){o=o.substr(11);a=true}else if(o.indexOf("js:")===0){o=o.substr(3);a=true}if(o.indexOf("{")!==0){o="{"+o+"}"}var s;if(a){s=Jt(e,function(){return Function("return ("+o+")")()},{})}else{s=S(o)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Wt(u(e),t,r,n)}function Jt(e,t,r){if(U.config.allowEval){return t()}else{J(e,"htmx:evalDisallowedError");return r}}function $t(e,t){return Wt(e,"hx-vars",true,t)}function Zt(e,t){return Wt(e,"hx-vals",false,t)}function Gt(e){return Q($t(e),Zt(e))}function Kt(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Yt(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){J(z().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function Qt(e,t){return e.getAllResponseHeaders().match(t)}function er(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||p(r,"String")){return Z(e,t,null,null,{targetOverride:k(r),returnPromise:true})}else{return Z(e,t,k(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:k(r.target),swapOverride:r.swap,returnPromise:true})}}else{return Z(e,t,null,null,{returnPromise:true})}}function tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Z(e,t,n,f,r){var c=null;var h=null;r=r!=null?r:{};if(r.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=z().body}var v=r.handler||rr;if(!Y(n)){return}var g=r.targetOverride||ne(n);if(g==null||g==te){J(n,"htmx:targetError",{target:V(n,"hx-target")});return}var p=n;var i=_(n);var o=G(n,"hx-sync");var m=null;var x=false;if(o){var y=o.split(":");var b=y[0].trim();if(b==="this"){p=re(n,"hx-sync")}else{p=ee(n,b)}o=(y[1]||"drop").trim();i=_(p);if(o==="drop"&&i.xhr&&i.abortable!==true){return}else if(o==="abort"){if(i.xhr){return}else{x=true}}else if(o==="replace"){$(p,"htmx:abort")}else if(o.indexOf("queue")===0){var w=o.split(" ");m=(w[1]||"last").trim()}}if(i.xhr){if(i.abortable){$(p,"htmx:abort")}else{if(m==null){if(f){var S=_(f);if(S&&S.triggerSpec&&S.triggerSpec.queue){m=S.triggerSpec.queue}}if(m==null){m="last"}}if(i.queuedRequests==null){i.queuedRequests=[]}if(m==="first"&&i.queuedRequests.length===0){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="all"){i.queuedRequests.push(function(){Z(e,t,n,f,r)})}else if(m==="last"){i.queuedRequests=[];i.queuedRequests.push(function(){Z(e,t,n,f,r)})}return}}var a=new XMLHttpRequest;i.xhr=a;i.abortable=x;var s=function(){i.xhr=null;i.abortable=false;if(i.queuedRequests!=null&&i.queuedRequests.length>0){var e=i.queuedRequests.shift();e()}};var E=G(n,"hx-prompt");if(E){var C=prompt(E);if(C===null||!$(n,"htmx:prompt",{prompt:C,target:g})){K(c);s();return d}}var R=G(n,"hx-confirm");if(R){if(!confirm(R)){K(c);s();return d}}var O=Xt(n,g,C);if(r.headers){O=Q(O,r.headers)}var L=Mt(n,e);var q=L.errors;var A=L.values;if(r.values){A=Q(A,r.values)}var T=Gt(n);var H=Q(A,T);var N=jt(H,n);if(e!=="get"&&G(n,"hx-encoding")==null){O["Content-Type"]="application/x-www-form-urlencoded"}if(t==null||t===""){t=z().location.href}var k=Wt(n,"hx-request");var l={parameters:N,unfilteredParameters:H,headers:O,target:g,verb:e,errors:q,withCredentials:r.credentials||k.credentials||U.config.withCredentials,timeout:r.timeout||k.timeout||U.config.timeout,path:t,triggeringEvent:f};if(!$(n,"htmx:configRequest",l)){K(c);s();return d}t=l.path;e=l.verb;O=l.headers;N=l.parameters;q=l.errors;if(q&&q.length>0){$(n,"htmx:validation:halted",l);K(c);s();return d}var I=t.split("#");var M=I[0];var D=I[1];if(e==="get"){var F=M;var P=Object.keys(N).length!==0;if(P){if(F.indexOf("?")<0){F+="?"}else{F+="&"}F+=Ft(N);if(D){F+="#"+D}}a.open("GET",F,true)}else{a.open(e.toUpperCase(),t,true)}a.overrideMimeType("text/html");a.withCredentials=l.withCredentials;a.timeout=l.timeout;if(k.noHeaders){}else{for(var X in O){if(O.hasOwnProperty(X)){var j=O[X];Kt(a,X,j)}}}var u={xhr:a,target:g,requestConfig:l,etc:r,pathInfo:{path:t,finalPath:F,anchor:D}};a.onload=function(){try{var e=tr(n);v(n,u);Tt(B);$(n,"htmx:afterRequest",u);$(n,"htmx:afterOnLoad",u);if(!Y(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(Y(r)){t=r}}if(t){$(t,"htmx:afterRequest",u);$(t,"htmx:afterOnLoad",u)}}K(c);s()}catch(e){J(n,"htmx:onLoadError",Q({error:e},u));throw e}};a.onerror=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendError",u);K(h);s()};a.onabort=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:sendAbort",u);K(h);s()};a.ontimeout=function(){Tt(B);J(n,"htmx:afterRequest",u);J(n,"htmx:timeout",u);K(h);s()};if(!$(n,"htmx:beforeRequest",u)){K(c);s();return d}var B=At(n);W(["loadstart","loadend","progress","abort"],function(t){W([a,a.upload],function(e){e.addEventListener(t,function(e){$(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});$(n,"htmx:beforeSend",u);a.send(e==="get"?null:Vt(a,n,N));return d}function rr(s,l){var u=l.xhr;var f=l.target;var r=l.etc;if(!$(s,"htmx:beforeOnLoad",l))return;if(Qt(u,/HX-Trigger:/i)){Se(u,"HX-Trigger",s)}if(Qt(u,/HX-Push:/i)){var c=u.getResponseHeader("HX-Push")}if(Qt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(Qt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(Qt(u,/HX-Retarget:/i)){l.target=z().querySelector(u.getResponseHeader("HX-Retarget"))}var h;if(c=="false"){h=false}else{h=Lt(s)||c}var n=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var e=u.status>=400;var t=Q({shouldSwap:n,serverResponse:d,isError:e},l);if(!$(f,"htmx:beforeSwap",t))return;f=t.target;d=t.serverResponse;e=t.isError;l.failed=e;l.successful=!e;if(t.shouldSwap){if(u.status===286){Ie(s)}gt(s,function(e){d=e.transformResponse(d,u,s)});if(h){St()}var i=r.swapOverride;var v=Ut(s,i);f.classList.add(U.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=zt(f);we(v.swapStyle,f,s,d,n);if(t.elt&&!Y(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!U.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){r.setSelectionRange(t.start,t.end)}r.focus(i)}}f.classList.remove(U.config.swappingClass);W(n.elts,function(e){if(e.classList){e.classList.add(U.config.settlingClass)}$(e,"htmx:afterSwap",l)});if(l.pathInfo.anchor){location.hash=l.pathInfo.anchor}if(Qt(u,/HX-Trigger-After-Swap:/i)){var o=s;if(!Y(s)){o=z().body}Se(u,"HX-Trigger-After-Swap",o)}var a=function(){W(n.tasks,function(e){e.call()});W(n.elts,function(e){if(e.classList){e.classList.remove(U.config.settlingClass)}$(e,"htmx:afterSettle",l)});if(h){var e=c||qt(s)||Yt(u)||l.pathInfo.finalPath||l.pathInfo.path;Et(e);$(z().body,"htmx:pushedIntoHistory",{path:e})}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}_t(n.elts,v);if(Qt(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!Y(s)){r=z().body}Se(u,"HX-Trigger-After-Settle",r)}};if(v.settleDelay>0){setTimeout(a,v.settleDelay)}else{a()}}catch(e){J(s,"htmx:swapError",l);throw e}};if(v.swapDelay>0){setTimeout(o,v.swapDelay)}else{o()}}if(e){J(s,"htmx:responseError",Q({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.path},l))}}var nr={};function ir(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function or(e,t){if(t.init){t.init(r)}nr[e]=Q(ir(),t)}function ar(e){delete nr[e]}function sr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=V(e,"hx-ext");if(t){W(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=nr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return sr(u(e),r,n)}function lr(e){if(z().readyState!=="loading"){e()}else{z().addEventListener("DOMContentLoaded",e)}}function ur(){if(U.config.includeIndicatorStyles!==false){z().head.insertAdjacentHTML("beforeend","")}}function fr(){var e=z().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function cr(){var e=fr();if(e){U.config=Q(U.config,e)}}lr(function(){cr();ur();var e=z().body;ct(e);var t=z().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=_(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){Ot();W(t,function(e){$(e,"htmx:restored",{document:z(),triggerEvent:$})})}};setTimeout(function(){$(e,"htmx:load",{})},0)});return U}()}); \ No newline at end of file +// /////////////////////////////////////////////////////////////////// +// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.min.js +// +(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 Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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,allowScriptTags: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,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.10"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(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 A(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 a(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=re().createDocumentFragment()}return i}function N(e){return/",0);return i.querySelector("template").content}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return a(""+n+"
",1);case"col":return a(""+n+"
",2);case"tr":return a(""+n+"
",2);case"td":case"th":return a(""+n+"
",3);case"script":case"style":return a("
"+n+"
",1);default:return a(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=g(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=g(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=g(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 $(e,t){e=g(e);e.classList.toggle(t)}function W(e,t){e=g(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=g(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 s(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(s(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function g(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:g(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var me=re().createElement("output");function pe(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[me]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.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=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function m(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){mt(e)})}},200)}}function mt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function pt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){p(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);mt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=pe(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=pe(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return pr(n)}else{return mr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:g(r),returnPromise:true})}else{return he(e,t,g(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:g(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=s(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==me){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var m=ne(n,"hx-sync");var p=null;var x=false;if(m){var B=m.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}m=(B[1]||"drop").trim();f=ae(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(m==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(m==="replace"){ce(g,"htmx:abort")}else if(m.indexOf("queue")===0){var V=m.split(" ");p=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(p==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=mr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var m=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:m},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;m=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){m=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var p=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!m){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(p)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){p=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file From 825a201b1883ba70c1a3a1fcda08d83545fe9a17 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Wed, 13 Mar 2024 10:10:57 -0700 Subject: [PATCH 12/18] Fixes sys.gettrace() is not being used anymore, closes #8 --- code/ch4_app/ch4_final_video_collector/app.py | 5 +++++ code/ch4_app/ch4_starter_video_collector/app.py | 5 +++++ code/ch5_partials/ch5_final_video_collector/app.py | 5 +++++ code/ch5_partials/ch5_starter_video_collector/app.py | 5 +++++ code/ch6_active_search/ch6_final_video_collector/app.py | 5 +++++ code/ch6_active_search/ch6_starter_video_collector/app.py | 5 +++++ code/ch7_infinite_scroll/ch7_final_video_collector/app.py | 6 ++++++ code/ch7_infinite_scroll/ch7_starter_video_collector/app.py | 5 +++++ code/starter_video_collector/app.py | 5 +++++ 9 files changed, 46 insertions(+) diff --git a/code/ch4_app/ch4_final_video_collector/app.py b/code/ch4_app/ch4_final_video_collector/app.py index dced78d..71fa232 100644 --- a/code/ch4_app/ch4_final_video_collector/app.py +++ b/code/ch4_app/ch4_final_video_collector/app.py @@ -47,7 +47,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch4_app/ch4_starter_video_collector/app.py b/code/ch4_app/ch4_starter_video_collector/app.py index dced78d..71fa232 100644 --- a/code/ch4_app/ch4_starter_video_collector/app.py +++ b/code/ch4_app/ch4_starter_video_collector/app.py @@ -47,7 +47,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch5_partials/ch5_final_video_collector/app.py b/code/ch5_partials/ch5_final_video_collector/app.py index 4dd198b..c4963a5 100644 --- a/code/ch5_partials/ch5_final_video_collector/app.py +++ b/code/ch5_partials/ch5_final_video_collector/app.py @@ -49,7 +49,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch5_partials/ch5_starter_video_collector/app.py b/code/ch5_partials/ch5_starter_video_collector/app.py index dced78d..71fa232 100644 --- a/code/ch5_partials/ch5_starter_video_collector/app.py +++ b/code/ch5_partials/ch5_starter_video_collector/app.py @@ -47,7 +47,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch6_active_search/ch6_final_video_collector/app.py b/code/ch6_active_search/ch6_final_video_collector/app.py index 4dd198b..c4963a5 100644 --- a/code/ch6_active_search/ch6_final_video_collector/app.py +++ b/code/ch6_active_search/ch6_final_video_collector/app.py @@ -49,7 +49,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch6_active_search/ch6_starter_video_collector/app.py b/code/ch6_active_search/ch6_starter_video_collector/app.py index 4dd198b..c4963a5 100644 --- a/code/ch6_active_search/ch6_starter_video_collector/app.py +++ b/code/ch6_active_search/ch6_starter_video_collector/app.py @@ -49,7 +49,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/ch7_infinite_scroll/ch7_final_video_collector/app.py b/code/ch7_infinite_scroll/ch7_final_video_collector/app.py index 4dd198b..1a92d31 100644 --- a/code/ch7_infinite_scroll/ch7_final_video_collector/app.py +++ b/code/ch7_infinite_scroll/ch7_final_video_collector/app.py @@ -49,7 +49,13 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + + print(f'{being_debugged=}') app.run(debug=being_debugged) else: configure() diff --git a/code/ch7_infinite_scroll/ch7_starter_video_collector/app.py b/code/ch7_infinite_scroll/ch7_starter_video_collector/app.py index 4dd198b..c4963a5 100644 --- a/code/ch7_infinite_scroll/ch7_starter_video_collector/app.py +++ b/code/ch7_infinite_scroll/ch7_starter_video_collector/app.py @@ -49,7 +49,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() diff --git a/code/starter_video_collector/app.py b/code/starter_video_collector/app.py index dced78d..71fa232 100644 --- a/code/starter_video_collector/app.py +++ b/code/starter_video_collector/app.py @@ -47,7 +47,12 @@ def setup_db(): if __name__ == '__main__': configure() + + # Python 3.12 has a new way to determine this, + # see https://github.com/talkpython/htmx-python-course/issues/8#issuecomment-1990894657 being_debugged = sys.gettrace() is not None + being_debugged = being_debugged or sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None + app.run(debug=being_debugged) else: configure() From 5363b79926fa84ac944960e2f2b842e445f60800 Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 1 Jul 2024 11:46:53 -0700 Subject: [PATCH 13/18] Update to the latest dependencies. --- requirements.txt | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index 959604b..7c7d2f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,38 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements.piptools --output-file requirements.txt -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -blinker==1.7.0 +blinker==1.8.2 # via flask click==8.1.7 # via flask -flask==3.0.2 -itsdangerous==2.1.2 +flask==3.0.3 + # via -r requirements.piptools +itsdangerous==2.2.0 # via flask -jinja-partials==0.2.0 -jinja2==3.1.3 +jinja-partials==0.2.1 + # via -r requirements.piptools +jinja2==3.1.4 # via + # -r requirements.piptools # flask # jinja-partials markupsafe==2.1.5 # via + # -r requirements.piptools # jinja2 # werkzeug -more-itertools==10.2.0 -pydantic==2.6.3 -pydantic-core==2.16.3 +more-itertools==10.3.0 + # via -r requirements.piptools +pydantic==2.8.0 + # via -r requirements.piptools +pydantic-core==2.20.0 # via pydantic -typing-extensions==4.10.0 +typing-extensions==4.12.2 # via # pydantic # pydantic-core -werkzeug==3.0.1 - # via flask +werkzeug==3.0.3 + # via + # -r requirements.piptools + # flask From ba5d8ae63c3160ddec42c4d3d471d7b96621b61a Mon Sep 17 00:00:00 2001 From: Michael Kennedy Date: Mon, 1 Jul 2024 11:53:28 -0700 Subject: [PATCH 14/18] Upgrade to the 2.0 release of HTMX (no code changes needed) --- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- .../templates/shared/_layout.html | 2 +- .../static/js/htmx.d.ts | 195 + .../starter_video_collector/static/js/htmx.js | 8890 ++++++++++------- .../static/js/htmx.min.js | 6 +- 34 files changed, 47284 insertions(+), 34549 deletions(-) create mode 100644 code/ch4_app/ch4_final_video_collector/static/js/htmx.d.ts create mode 100644 code/ch4_app/ch4_starter_video_collector/static/js/htmx.d.ts create mode 100644 code/ch5_partials/ch5_final_video_collector/static/js/htmx.d.ts create mode 100644 code/ch5_partials/ch5_starter_video_collector/static/js/htmx.d.ts create mode 100644 code/ch6_active_search/ch6_final_video_collector/static/js/htmx.d.ts create mode 100644 code/ch6_active_search/ch6_starter_video_collector/static/js/htmx.d.ts create mode 100644 code/ch7_infinite_scroll/ch7_final_video_collector/static/js/htmx.d.ts create mode 100644 code/ch7_infinite_scroll/ch7_starter_video_collector/static/js/htmx.d.ts create mode 100644 code/starter_video_collector/static/js/htmx.d.ts diff --git a/code/ch4_app/ch4_final_video_collector/static/js/htmx.d.ts b/code/ch4_app/ch4_final_video_collector/static/js/htmx.d.ts new file mode 100644 index 0000000..3775459 --- /dev/null +++ b/code/ch4_app/ch4_final_video_collector/static/js/htmx.d.ts @@ -0,0 +1,195 @@ +declare namespace htmx { + const onLoad: (callback: (elt: Node) => void) => EventListener; + const process: (elt: string | Element) => void; + const on: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener; + const off: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener; + const trigger: (elt: string | EventTarget, eventName: string, detail?: any) => boolean; + const ajax: (verb: HttpVerb, path: string, context: string | Element | HtmxAjaxHelperContext) => Promise; + const find: (eltOrSelector: string | ParentNode, selector?: string) => Element; + const findAll: (eltOrSelector: string | ParentNode, selector?: string) => NodeListOf; + const closest: (elt: string | Element, selector: string) => Element; + function values(elt: Element, type: HttpVerb): any; + const remove: (elt: Node, delay?: number) => void; + const addClass: (elt: string | Element, clazz: string, delay?: number) => void; + const removeClass: (node: string | Node, clazz: string, delay?: number) => void; + const toggleClass: (elt: string | Element, clazz: string) => void; + const takeClass: (elt: string | Node, clazz: string) => void; + const swap: (target: string | Element, content: string, swapSpec: HtmxSwapSpecification, swapOptions?: SwapOptions) => void; + const defineExtension: (name: string, extension: any) => void; + const removeExtension: (name: string) => void; + const logAll: () => void; + const logNone: () => void; + const logger: any; + namespace config { + const historyEnabled: boolean; + const historyCacheSize: number; + const refreshOnHistoryMiss: boolean; + const defaultSwapStyle: HtmxSwapStyle; + const defaultSwapDelay: number; + const defaultSettleDelay: number; + const includeIndicatorStyles: boolean; + const indicatorClass: string; + const requestClass: string; + const addedClass: string; + const settlingClass: string; + const swappingClass: string; + const allowEval: boolean; + const allowScriptTags: boolean; + const inlineScriptNonce: string; + const inlineStyleNonce: string; + const attributesToSettle: string[]; + const withCredentials: boolean; + const timeout: number; + const wsReconnectDelay: "full-jitter" | ((retryCount: number) => number); + const wsBinaryType: BinaryType; + const disableSelector: string; + const scrollBehavior: 'auto' | 'instant' | 'smooth'; + const defaultFocusScroll: boolean; + const getCacheBusterParam: boolean; + const globalViewTransitions: boolean; + const methodsThatUseUrlParams: (HttpVerb)[]; + const selfRequestsOnly: boolean; + const ignoreTitle: boolean; + const scrollIntoViewOnBoost: boolean; + const triggerSpecsCache: any | null; + const disableInheritance: boolean; + const responseHandling: HtmxResponseHandlingConfig[]; + const allowNestedOobSwaps: boolean; + } + const parseInterval: (str: string) => number; + const _: (str: string) => any; + const version: string; +} +type HttpVerb = 'get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch'; +type SwapOptions = { + select?: string; + selectOOB?: string; + eventInfo?: any; + anchor?: string; + contextElement?: Element; + afterSwapCallback?: swapCallback; + afterSettleCallback?: swapCallback; +}; +type swapCallback = () => any; +type HtmxSwapStyle = 'innerHTML' | 'outerHTML' | 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend' | 'delete' | 'none' | string; +type HtmxSwapSpecification = { + swapStyle: HtmxSwapStyle; + swapDelay: number; + settleDelay: number; + transition?: boolean; + ignoreTitle?: boolean; + head?: string; + scroll?: 'top' | 'bottom'; + scrollTarget?: string; + show?: string; + showTarget?: string; + focusScroll?: boolean; +}; +type ConditionalFunction = ((this: Node, evt: Event) => boolean) & { + source: string; +}; +type HtmxTriggerSpecification = { + trigger: string; + pollInterval?: number; + eventFilter?: ConditionalFunction; + changed?: boolean; + once?: boolean; + consume?: boolean; + delay?: number; + from?: string; + target?: string; + throttle?: number; + queue?: string; + root?: string; + threshold?: string; +}; +type HtmxElementValidationError = { + elt: Element; + message: string; + validity: ValidityState; +}; +type HtmxHeaderSpecification = Record; +type HtmxAjaxHelperContext = { + source?: Element | string; + event?: Event; + handler?: HtmxAjaxHandler; + target: Element | string; + swap?: HtmxSwapStyle; + values?: any | FormData; + headers?: Record; + select?: string; +}; +type HtmxRequestConfig = { + boosted: boolean; + useUrlParams: boolean; + formData: FormData; + /** + * formData proxy + */ + parameters: any; + unfilteredFormData: FormData; + /** + * unfilteredFormData proxy + */ + unfilteredParameters: any; + headers: HtmxHeaderSpecification; + target: Element; + verb: HttpVerb; + errors: HtmxElementValidationError[]; + withCredentials: boolean; + timeout: number; + path: string; + triggeringEvent: Event; +}; +type HtmxResponseInfo = { + xhr: XMLHttpRequest; + target: Element; + requestConfig: HtmxRequestConfig; + etc: HtmxAjaxEtc; + boosted: boolean; + select: string; + pathInfo: { + requestPath: string; + finalRequestPath: string; + responsePath: string | null; + anchor: string; + }; + failed?: boolean; + successful?: boolean; +}; +type HtmxAjaxEtc = { + returnPromise?: boolean; + handler?: HtmxAjaxHandler; + select?: string; + targetOverride?: Element; + swapOverride?: HtmxSwapStyle; + headers?: Record; + values?: any | FormData; + credentials?: boolean; + timeout?: number; +}; +type HtmxResponseHandlingConfig = { + code?: string; + swap: boolean; + error?: boolean; + ignoreTitle?: boolean; + select?: string; + target?: string; + swapOverride?: string; + event?: string; +}; +type HtmxBeforeSwapDetails = HtmxResponseInfo & { + shouldSwap: boolean; + serverResponse: any; + isError: boolean; + ignoreTitle: boolean; + selectOverride: string; +}; +type HtmxAjaxHandler = (elt: Element, responseInfo: HtmxResponseInfo) => any; +type HtmxSettleTask = (() => void); +type HtmxSettleInfo = { + tasks: HtmxSettleTask[]; + elts: Element[]; + title?: string; +}; +type HtmxExtension = any; diff --git a/code/ch4_app/ch4_final_video_collector/static/js/htmx.js b/code/ch4_app/ch4_final_video_collector/static/js/htmx.js index 86e7668..c57bcd7 100644 --- a/code/ch4_app/ch4_final_video_collector/static/js/htmx.js +++ b/code/ch4_app/ch4_final_video_collector/static/js/htmx.js @@ -1,3909 +1,5131 @@ -// /////////////////////////////////////////////////////////////////// -// HTMX v1.9.10 from https://unpkg.com/htmx.org@1.9.10/dist/htmx.js -// - -// UMD insanity -// This code sets up support for (in order) AMD, ES6 modules, and globals. -(function (root, factory) { - //@ts-ignore - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - //@ts-ignore - define([], factory); - } else if (typeof module === 'object' && module.exports) { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals - root.htmx = root.htmx || factory(); - } -}(typeof self !== 'undefined' ? self : this, function () { -return (function () { - 'use strict'; - - // Public API - //** @type {import("./htmx").HtmxApi} */ - // TODO: list all methods in public API - var htmx = { - onLoad: onLoadHelper, - process: processNode, - on: addEventListenerImpl, - off: removeEventListenerImpl, - trigger : triggerEvent, - ajax : ajaxHelper, - find : find, - findAll : findAll, - closest : closest, - values : function(elt, type){ - var inputValues = getInputValues(elt, type || "post"); - return inputValues.values; - }, - remove : removeElement, - addClass : addClassToElement, - removeClass : removeClassFromElement, - toggleClass : toggleClassOnElement, - takeClass : takeClassForElement, - defineExtension : defineExtension, - removeExtension : removeExtension, - logAll : logAll, - logNone : logNone, - 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, - allowScriptTags: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, - methodsThatUseUrlParams: ["get"], - selfRequestsOnly: false, - ignoreTitle: false, - scrollIntoViewOnBoost: true, - triggerSpecsCache: null, - }, - parseInterval:parseInterval, - _:internalEval, - createEventSource: function(url){ - return new EventSource(url, {withCredentials:true}) - }, - createWebSocket: function(url){ - var sock = new WebSocket(url, []); - sock.binaryType = htmx.config.wsBinaryType; - return sock; - }, - version: "1.9.10" - }; - - /** @type {import("./htmx").HtmxInternalApi} */ - var internalAPI = { - addTriggerHandler: addTriggerHandler, - bodyContains: bodyContains, - canAccessLocalStorage: canAccessLocalStorage, - findThisElement: findThisElement, - filterValues: filterValues, - hasAttribute: hasAttribute, - getAttributeValue: getAttributeValue, - getClosestAttributeValue: getClosestAttributeValue, - getClosestMatch: getClosestMatch, - getExpressionVars: getExpressionVars, - getHeaders: getHeaders, - getInputValues: getInputValues, - getInternalData: getInternalData, - getSwapSpecification: getSwapSpecification, - getTriggerSpecs: getTriggerSpecs, - getTarget: getTarget, - makeFragment: makeFragment, - mergeObjects: mergeObjects, - makeSettleInfo: makeSettleInfo, - oobSwap: oobSwap, - querySelectorExt: querySelectorExt, - selectAndSwap: selectAndSwap, - settleImmediately: settleImmediately, - shouldCancel: shouldCancel, - triggerEvent: triggerEvent, - triggerErrorEvent: triggerErrorEvent, - withExtensions: withExtensions, - } - - var VERBS = ['get', 'post', 'put', 'delete', 'patch']; - var VERB_SELECTOR = VERBS.map(function(verb){ - return "[hx-" + verb + "], [data-hx-" + verb + "]" - }).join(", "); - - var HEAD_TAG_REGEX = makeTagRegEx('head'), - TITLE_TAG_REGEX = makeTagRegEx('title'), - SVG_TAGS_REGEX = makeTagRegEx('svg', true); - - //==================================================================== - // Utilities - //==================================================================== - - /** - * @param {string} tag - * @param {boolean} global - * @returns {RegExp} - */ - function makeTagRegEx(tag, global = false) { - return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, - global ? 'gim' : 'im'); - } - - function parseInterval(str) { - if (str == undefined) { - return undefined; - } - - let interval = NaN; - if (str.slice(-2) == "ms") { - interval = parseFloat(str.slice(0, -2)); - } else if (str.slice(-1) == "s") { - interval = parseFloat(str.slice(0, -1)) * 1000; - } else if (str.slice(-1) == "m") { - interval = parseFloat(str.slice(0, -1)) * 1000 * 60; - } else { - interval = parseFloat(str); - } - return isNaN(interval) ? undefined : interval; - } - - /** - * @param {HTMLElement} elt - * @param {string} name - * @returns {(string | null)} - */ - function getRawAttribute(elt, name) { - return elt.getAttribute && elt.getAttribute(name); - } - - // resolve with both hx and data-hx prefixes - function hasAttribute(elt, qualifiedName) { - return elt.hasAttribute && (elt.hasAttribute(qualifiedName) || - elt.hasAttribute("data-" + qualifiedName)); - } - - /** - * - * @param {HTMLElement} elt - * @param {string} qualifiedName - * @returns {(string | null)} - */ - function getAttributeValue(elt, qualifiedName) { - return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName); - } - - /** - * @param {HTMLElement} elt - * @returns {HTMLElement | null} - */ - function parentElt(elt) { - return elt.parentElement; - } - - /** - * @returns {Document} - */ - function getDocument() { - return document; - } - - /** - * @param {HTMLElement} elt - * @param {(e:HTMLElement) => boolean} condition - * @returns {HTMLElement | null} - */ - function getClosestMatch(elt, condition) { - while (elt && !condition(elt)) { - elt = parentElt(elt); - } - - return elt ? elt : null; - } - - function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName){ - var attributeValue = getAttributeValue(ancestor, attributeName); - var disinherit = getAttributeValue(ancestor, "hx-disinherit"); - if (initialElement !== ancestor && disinherit && (disinherit === "*" || disinherit.split(" ").indexOf(attributeName) >= 0)) { - return "unset"; - } else { - return attributeValue - } - } - - /** - * @param {HTMLElement} elt - * @param {string} attributeName - * @returns {string | null} - */ - function getClosestAttributeValue(elt, attributeName) { - var closestAttr = null; - getClosestMatch(elt, function (e) { - return closestAttr = getAttributeValueWithDisinheritance(elt, e, attributeName); - }); - if (closestAttr !== "unset") { - return closestAttr; - } - } - - /** - * @param {HTMLElement} elt - * @param {string} selector - * @returns {boolean} - */ - function matches(elt, selector) { - // @ts-ignore: non-standard properties for browser compatibility - // noinspection JSUnresolvedVariable - var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; - return matchesFunction && matchesFunction.call(elt, selector); - } - - /** - * @param {string} str - * @returns {string} - */ - function getStartTag(str) { - var tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i - var match = tagMatcher.exec( str ); - if (match) { - return match[1].toLowerCase(); - } else { - return ""; - } - } - - /** - * - * @param {string} resp - * @param {number} depth - * @returns {Element} - */ - function parseHTML(resp, depth) { - var parser = new DOMParser(); - var responseDoc = parser.parseFromString(resp, "text/html"); - - /** @type {Element} */ - var responseNode = responseDoc.body; - while (depth > 0) { - depth--; - // @ts-ignore - responseNode = responseNode.firstChild; - } - if (responseNode == null) { - // @ts-ignore - responseNode = getDocument().createDocumentFragment(); - } - return responseNode; - } - - function aFullPageResponse(resp) { - return / number)} + * @default "full-jitter" + */ + wsReconnectDelay: 'full-jitter', + /** + * The type of binary data being received over the WebSocket connection + * @type BinaryType + * @default 'blob' + */ + wsBinaryType: 'blob', + /** + * @type string + * @default '[hx-disable], [data-hx-disable]' + */ + disableSelector: '[hx-disable], [data-hx-disable]', + /** + * @type {'auto' | 'instant' | 'smooth'} + * @default 'smooth' + */ + scrollBehavior: 'instant', + /** + * If the focused element should be scrolled into view. + * @type boolean + * @default false + */ + defaultFocusScroll: false, + /** + * If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser + * @type boolean + * @default false + */ + getCacheBusterParam: false, + /** + * If set to true, htmx will use the View Transition API when swapping in new content. + * @type boolean + * @default false + */ + globalViewTransitions: false, + /** + * htmx will format requests with these methods by encoding their parameters in the URL, not the request body + * @type {(HttpVerb)[]} + * @default ['get', 'delete'] + */ + methodsThatUseUrlParams: ['get', 'delete'], + /** + * If set to true, disables htmx-based requests to non-origin hosts. + * @type boolean + * @default false + */ + selfRequestsOnly: true, + /** + * If set to true htmx will not update the title of the document when a title tag is found in new content + * @type boolean + * @default false + */ + ignoreTitle: false, + /** + * Whether the target of a boosted element is scrolled into the viewport. + * @type boolean + * @default true + */ + scrollIntoViewOnBoost: true, + /** + * The cache to store evaluated trigger specifications into. + * You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) + * @type {Object|null} + * @default null + */ + triggerSpecsCache: null, + /** @type boolean */ + disableInheritance: false, + /** @type HtmxResponseHandlingConfig[] */ + responseHandling: [ + { code: '204', swap: false }, + { code: '[23]..', swap: true }, + { code: '[45]..', swap: false, error: true } + ], + /** + * Whether to process OOB swaps on elements that are nested within the main response element. + * @type boolean + * @default true + */ + allowNestedOobSwaps: true + }, + /** @type {typeof parseInterval} */ + parseInterval: null, + /** @type {typeof internalEval} */ + _: null, + version: '2.0.0' + } + // Tsc madness part 2 + htmx.onLoad = onLoadHelper + htmx.process = processNode + htmx.on = addEventListenerImpl + htmx.off = removeEventListenerImpl + htmx.trigger = triggerEvent + htmx.ajax = ajaxHelper + htmx.find = find + htmx.findAll = findAll + htmx.closest = closest + htmx.remove = removeElement + htmx.addClass = addClassToElement + htmx.removeClass = removeClassFromElement + htmx.toggleClass = toggleClassOnElement + htmx.takeClass = takeClassForElement + htmx.swap = swap + htmx.defineExtension = defineExtension + htmx.removeExtension = removeExtension + htmx.logAll = logAll + htmx.logNone = logNone + htmx.parseInterval = parseInterval + htmx._ = internalEval + + const internalAPI = { + addTriggerHandler, + bodyContains, + canAccessLocalStorage, + findThisElement, + filterValues, + swap, + hasAttribute, + getAttributeValue, + getClosestAttributeValue, + getClosestMatch, + getExpressionVars, + getHeaders, + getInputValues, + getInternalData, + getSwapSpecification, + getTriggerSpecs, + getTarget, + makeFragment, + mergeObjects, + makeSettleInfo, + oobSwap, + querySelectorExt, + settleImmediately, + shouldCancel, + triggerEvent, + triggerErrorEvent, + withExtensions + } + + const VERBS = ['get', 'post', 'put', 'delete', 'patch'] + const VERB_SELECTOR = VERBS.map(function(verb) { + return '[hx-' + verb + '], [data-hx-' + verb + ']' + }).join(', ') + + const HEAD_TAG_REGEX = makeTagRegEx('head') + + //= =================================================================== + // Utilities + //= =================================================================== + + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im') + } + + /** + * Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes. + * + * Caution: Accepts an int followed by either **s** or **ms**. All other values use **parseFloat** + * + * @see https://htmx.org/api/#parseInterval + * + * @param {string} str timing string + * @returns {number|undefined} + */ + function parseInterval(str) { + if (str == undefined) { + return undefined + } - /** - * - * @param {string} response - * @returns {Element} - */ - function makeFragment(response) { - var partialResponse = !aFullPageResponse(response); - var startTag = getStartTag(response); - var content = response; - if (startTag === 'head') { - content = content.replace(HEAD_TAG_REGEX, ''); - } - if (htmx.config.useTemplateFragments && partialResponse) { - var documentFragment = parseHTML("", 0); - // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangeably? - return documentFragment.querySelector('template').content; - } - switch (startTag) { - case "thead": - case "tbody": - case "tfoot": - case "colgroup": - case "caption": - return parseHTML("" + content + "
", 1); - case "col": - return parseHTML("" + content + "
", 2); - case "tr": - return parseHTML("" + content + "
", 2); - case "td": - case "th": - return parseHTML("" + content + "
", 3); - case "script": - case "style": - return parseHTML("
" + content + "
", 1); - default: - return parseHTML(content, 0); - } - } + let interval = NaN + if (str.slice(-2) == 'ms') { + interval = parseFloat(str.slice(0, -2)) + } else if (str.slice(-1) == 's') { + interval = parseFloat(str.slice(0, -1)) * 1000 + } else if (str.slice(-1) == 'm') { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60 + } else { + interval = parseFloat(str) + } + return isNaN(interval) ? undefined : interval + } + + /** + * @param {Node} elt + * @param {string} name + * @returns {(string | null)} + */ + function getRawAttribute(elt, name) { + return elt instanceof Element && elt.getAttribute(name) + } + + /** + * @param {Element} elt + * @param {string} qualifiedName + * @returns {boolean} + */ + // resolve with both hx and data-hx prefixes + function hasAttribute(elt, qualifiedName) { + return !!elt.hasAttribute && (elt.hasAttribute(qualifiedName) || + elt.hasAttribute('data-' + qualifiedName)) + } + + /** + * + * @param {Node} elt + * @param {string} qualifiedName + * @returns {(string | null)} + */ + function getAttributeValue(elt, qualifiedName) { + return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, 'data-' + qualifiedName) + } + + /** + * @param {Node} elt + * @returns {Node | null} + */ + function parentElt(elt) { + const parent = elt.parentElement + if (!parent && elt.parentNode instanceof ShadowRoot) return elt.parentNode + return parent + } + + /** + * @returns {Document} + */ + function getDocument() { + return document + } + + /** + * @param {Node} elt + * @param {boolean} global + * @returns {Node|Document} + */ + function getRootNode(elt, global) { + return elt.getRootNode ? elt.getRootNode({ composed: global }) : getDocument() + } + + /** + * @param {Node} elt + * @param {(e:Node) => boolean} condition + * @returns {Node | null} + */ + function getClosestMatch(elt, condition) { + while (elt && !condition(elt)) { + elt = parentElt(elt) + } - /** - * @param {Function} func - */ - function maybeCall(func){ - if(func) { - func(); - } + return elt || null + } + + /** + * @param {Element} initialElement + * @param {Element} ancestor + * @param {string} attributeName + * @returns {string|null} + */ + function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) { + const attributeValue = getAttributeValue(ancestor, attributeName) + const disinherit = getAttributeValue(ancestor, 'hx-disinherit') + var inherit = getAttributeValue(ancestor, 'hx-inherit') + if (initialElement !== ancestor) { + if (htmx.config.disableInheritance) { + if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) { + return attributeValue + } else { + return null + } + } + if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) { + return 'unset' + } + } + return attributeValue + } + + /** + * @param {Element} elt + * @param {string} attributeName + * @returns {string | null} + */ + function getClosestAttributeValue(elt, attributeName) { + let closestAttr = null + getClosestMatch(elt, function(e) { + return !!(closestAttr = getAttributeValueWithDisinheritance(elt, asElement(e), attributeName)) + }) + if (closestAttr !== 'unset') { + return closestAttr + } + } + + /** + * @param {Node} elt + * @param {string} selector + * @returns {boolean} + */ + function matches(elt, selector) { + // @ts-ignore: non-standard properties for browser compatibility + // noinspection JSUnresolvedVariable + const matchesFunction = elt instanceof Element && (elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector) + return !!matchesFunction && matchesFunction.call(elt, selector) + } + + /** + * @param {string} str + * @returns {string} + */ + function getStartTag(str) { + const tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i + const match = tagMatcher.exec(str) + if (match) { + return match[1].toLowerCase() + } else { + return '' + } + } + + /** + * @param {string} resp + * @returns {Document} + */ + function parseHTML(resp) { + const parser = new DOMParser() + return parser.parseFromString(resp, 'text/html') + } + + /** + * @param {DocumentFragment} fragment + * @param {Node} elt + */ + function takeChildrenFor(fragment, elt) { + while (elt.childNodes.length > 0) { + fragment.append(elt.childNodes[0]) + } + } + + /** + * @param {HTMLScriptElement} script + * @returns {HTMLScriptElement} + */ + function duplicateScript(script) { + const newScript = getDocument().createElement('script') + forEach(script.attributes, function(attr) { + newScript.setAttribute(attr.name, attr.value) + }) + newScript.textContent = script.textContent + newScript.async = false + if (htmx.config.inlineScriptNonce) { + newScript.nonce = htmx.config.inlineScriptNonce + } + return newScript + } + + /** + * @param {HTMLScriptElement} script + * @returns {boolean} + */ + function isJavaScriptScriptNode(script) { + return script.matches('script') && (script.type === 'text/javascript' || script.type === 'module' || script.type === '') + } + + /** + * we have to make new copies of script tags that we are going to insert because + * SOME browsers (not saying who, but it involves an element and an animal) don't + * execute scripts created in