From e5c4ec6730a58b8c19a98ed6cae7edfcbea520f0 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 20:06:50 +1000 Subject: [PATCH 01/80] fix ci --- .drone.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.drone.yml b/.drone.yml index dd831fd..cc112c0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,6 +15,7 @@ steps: - name: pull into the folduh commands: - cd /usr/share/repos/$DRONE_REPO_NAME + - git checkout $DRONE_COMMIT_BRANCH - git pull origin $DRONE_COMMIT_BRANCH - name: build environment: From c5a4c8e6edbc0d0bb34b69a8d6071d56b84f9a86 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:22:14 +1000 Subject: [PATCH 02/80] check many hexes --- templates/interactive-posts.html | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 15a9558..ac1d1be 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -4,7 +4,24 @@ {% include 'navbar.html' %} - + +
+
+

Создай то, о чем будут говорить многие в /{{board}}/.

+
+ +

+ +

+

Загрузить картинку:

+ + +
Картинка: {{singlePic}}
+

+ +

+
+
From 421fecce4d495fc32b133ae6230810f150a7fb40 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:23:27 +1000 Subject: [PATCH 03/80] check many hexes --- templates/interactive-posts.html | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index ac1d1be..a7cba20 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,20 +6,22 @@ {% include 'navbar.html' %}
-
-

Создай то, о чем будут говорить многие в /{{board}}/.

-
- -

- -

-

Загрузить картинку:

- - -
Картинка: {{singlePic}}
-

- -

+
+
+

Создай то, о чем будут говорить многие в /{{board}}/.

+
+ +

+ +

+

Загрузить картинку:

+ + +
Картинка: {{singlePic}}
+

+ +

+
From 6e9587e074373a40cbb405a3b4c0d23e42a61435 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:24:39 +1000 Subject: [PATCH 04/80] check many hexes --- app.py | 21 ------------- templates/board.html | 26 --------------- templates/page1.html | 36 --------------------- templates/posts.html | 75 -------------------------------------------- templates/todo.html | 15 --------- 5 files changed, 173 deletions(-) delete mode 100644 templates/board.html delete mode 100644 templates/page1.html delete mode 100644 templates/posts.html delete mode 100644 templates/todo.html diff --git a/app.py b/app.py index e2091f6..2adab5a 100644 --- a/app.py +++ b/app.py @@ -109,28 +109,7 @@ def post_a_post(post_key): # def post_a_file(): # pass - -### stolen -@app.route("/todo") -def page_todo(): - return render_template("page1.html") - -@app.route("/search", methods=["POST"]) -def search_todo(): - search_term = request.form.get("search") - - if not len(search_term): - return render_template("todo.html", host_id=docker_short_id, todos=[]) - - res_todos = [] - for todo in todos: - if search_term in todo["title"]: - res_todos.append(todo) - - return render_template("todo.html", host_id=docker_short_id, todos=res_todos) -### /stolen - if __name__ == "__main__": diff --git a/templates/board.html b/templates/board.html deleted file mode 100644 index c12d10d..0000000 --- a/templates/board.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "base.html" %} - -{% block content %} - -
-

Вы тут: /{{board_id}}

-
- - - - - -{% include 'posts.html' %} - -{% endblock content %} \ No newline at end of file diff --git a/templates/page1.html b/templates/page1.html deleted file mode 100644 index d383a52..0000000 --- a/templates/page1.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "base.html" %} - -{% block content %} - -
- -
- - Searching... -
- +dev 3 - - - - - - - - - - - - {% include 'todo.html' %} - -
-
-{% endblock content %} \ No newline at end of file diff --git a/templates/posts.html b/templates/posts.html deleted file mode 100644 index 69ca274..0000000 --- a/templates/posts.html +++ /dev/null @@ -1,75 +0,0 @@ -{% if board_threads|length>0 %} - {% for thread in board_threads %} -
-
- - - {{thread.name}} - - - 05/07/23 Срд 14:53:58 - - - - {{thread.post_num}} - 2 - - - - - - -
-
- - - - - - - - - -
-
- {% for pic in thread.pic_links %} - - - - {% endfor %} -
-
-
- - -
- {% endfor %} -{% endif %} \ No newline at end of file diff --git a/templates/todo.html b/templates/todo.html deleted file mode 100644 index 24ea075..0000000 --- a/templates/todo.html +++ /dev/null @@ -1,15 +0,0 @@ -{% if todos|length>0 %} - {% for todo in todos %} - - {{todo.id}} - {{todo.title}} - - {% if todo.completed %} - Yes - {% else %} - No - {% endif %} - - - {% endfor %} -{% endif %} \ No newline at end of file From 113bd64b0e59a9b632a3db62785137d607d0c717 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:26:32 +1000 Subject: [PATCH 05/80] check many hexes --- templates/interactive-posts.html | 33 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index a7cba20..4e71736 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,21 +6,24 @@ {% include 'navbar.html' %}
-
-
-

Создай то, о чем будут говорить многие в /{{board}}/.

-
- -

- -

-

Загрузить картинку:

- - -
Картинка: {{singlePic}}
-

- -

+ +
+
+
+

Создай то, о чем будут говорить многие в /{{board}}/.

+
+ +

+ +

+

Загрузить картинку:

+ + +
Картинка: {{singlePic}}
+

+ +

+
From 75e07c2118e1ddc1b7d3b2dbd7b282017b9be1d1 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:26:41 +1000 Subject: [PATCH 06/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 4e71736..6c8c455 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -7,7 +7,7 @@
-
+

Создай то, о чем будут говорить многие в /{{board}}/.

From 5b25dced59a39e87c963c7179ee38eda73fa7974 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:27:23 +1000 Subject: [PATCH 07/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 6c8c455..39b48b5 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -29,7 +29,7 @@
-
+
{% include 'i-posts.html' %}
From 410861aa75b539015a7580ef73ba53e58e34b0fd Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:28:39 +1000 Subject: [PATCH 08/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 39b48b5..b42c7f8 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -29,7 +29,7 @@
-
+
{% include 'i-posts.html' %}
From ed851efd501d976e5250b337afeac9e204045812 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:30:11 +1000 Subject: [PATCH 09/80] check many hexes --- templates/interactive-posts.html | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index b42c7f8..e7f2c3a 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,22 +6,21 @@ {% include 'navbar.html' %}
-
-
-

Создай то, о чем будут говорить многие в /{{board}}/.

+
+

Создай то, о чем будут говорить многие.


- +

- +

Загрузить картинку:

Картинка: {{singlePic}}

- +

From a4ea21dfa6a1b41ad31c37d06470055a91883df8 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:31:26 +1000 Subject: [PATCH 10/80] check many hexes --- templates/interactive-posts.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index e7f2c3a..32fd3ed 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,7 +11,10 @@

Создай то, о чем будут говорить многие.


- +
+ + +

From c26c016adc4c05fcfd026403c4487a65ecd20e98 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:39:40 +1000 Subject: [PATCH 11/80] check many hexes --- templates/interactive-posts.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 32fd3ed..bb32036 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -15,16 +15,18 @@
-

- -

-

Загрузить картинку:

+ + + +
From 27be10ee32d22d3c72db44bc59a87400cb264758 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:41:09 +1000 Subject: [PATCH 12/80] check many hexes --- templates/interactive-posts.html | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index bb32036..7308442 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,14 +11,17 @@

Создай то, о чем будут говорить многие.


-
- - -
- - +
+
+ + +
+
+
+ +
-
+
-->
From 54bb686249d87483b126182d039ff827a3af9e7a Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:44:33 +1000 Subject: [PATCH 14/80] check many hexes --- templates/interactive-posts.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index eece45c..04c2aed 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -8,8 +8,7 @@
-
- +
Создай то, о чем будут говорить многие. add
From 919c860475b110d375a84f4f2e3e5a8a51cfae88 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:46:12 +1000 Subject: [PATCH 15/80] check many hexes --- templates/interactive-posts.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 04c2aed..3eb25d8 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -13,10 +13,13 @@ add
-
- - -
+ +
+
+ + +
+

-->
-
--> +
From ee869d46935dc70c3697da1d1ffd321bf778df06 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:50:48 +1000 Subject: [PATCH 19/80] check many hexes --- templates/interactive-posts.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 685ffc5..8a3cf7a 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -10,10 +10,8 @@
-
- Создай то, о чем будут говорить многие. - add -
+ Создай то, о чем будут говорить многие. + add
From f653d1f234b3afcbdfe367073d309039da03ca79 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:53:06 +1000 Subject: [PATCH 20/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 8a3cf7a..e23f734 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,7 +11,7 @@
Создай то, о чем будут говорить многие. - add + >>{{ postos[0]._key }}
From e96ff651cc4b36ca954da4cadb2bdc7567cf25ea Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:53:27 +1000 Subject: [PATCH 21/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index e23f734..e17372e 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,7 +11,7 @@
Создай то, о чем будут говорить многие. - >>{{ postos[0]._key }} + >>{{ postos[0]._key }}
From ca15046bda41d85e4184868e8edbb2222bd42a7a Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:54:45 +1000 Subject: [PATCH 22/80] check many hexes --- templates/interactive-posts.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index e17372e..a432d54 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -8,10 +8,12 @@
+
Создай то, о чем будут говорить многие. - >>{{ postos[0]._key }}
From 622fa4f50d7b3848b05f40b6ad5fa300e6479826 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:55:48 +1000 Subject: [PATCH 23/80] check many hexes --- templates/interactive-posts.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index a432d54..4cf5efd 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -8,12 +8,10 @@
-
Создай то, о чем будут говорить многие. + >>
From ed2cf28893977bf8922dc896a371add7f7d39cf4 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:59:10 +1000 Subject: [PATCH 24/80] check many hexes --- app.py | 19 ++++++++----------- templates/interactive-posts.html | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/app.py b/app.py index 2adab5a..49e4f96 100644 --- a/app.py +++ b/app.py @@ -48,23 +48,20 @@ def healthz(): def homepage(): return render_template("main-page.html", host_id=docker_short_id, boards=board_list) - -@app.route("/boards/") -def page_board(board_id): - - b_threads = [ open_threads[thread_id] for thread_id in open_threads if thread_id in threads_lists[board_id]] - return render_template("board.html", host_id=docker_short_id, board_id=board_id, boards=board_list, board_threads=b_threads) + - -@app.route("/db_posts") -def page_posts_from_db(): +@app.route("/db_posts/") +def page_posts_from_db(target_post_id=None): postos = db.collection('posts') postos = [ p for p in postos] postos = sorted(postos, key=lambda posto: posto['_key'], reverse=False) - - return render_template("interactive-posts.html", host_id=docker_short_id, postos=postos) + + if not target_post_id: + target_post_id = postos[0]._key + + return render_template("interactive-posts.html", host_id=docker_short_id, postos=postos, target_post_id=target_post_id) @app.route('/post_my_post/', methods=['POST']) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 4cf5efd..d641a35 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,7 +11,7 @@
Создай то, о чем будут говорить многие. - >> + >>
From a807114c5827be9349c6783822b3d2fcd7f22c82 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:00:22 +1000 Subject: [PATCH 25/80] check many hexes --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 49e4f96..a739e51 100644 --- a/app.py +++ b/app.py @@ -50,7 +50,7 @@ def homepage(): - +@app.route('/db_posts') @app.route("/db_posts/") def page_posts_from_db(target_post_id=None): From feec65123ab779b2e72351dca53f590675ef15f9 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:01:15 +1000 Subject: [PATCH 26/80] check many hexes --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index a739e51..403739a 100644 --- a/app.py +++ b/app.py @@ -59,7 +59,7 @@ def page_posts_from_db(target_post_id=None): postos = sorted(postos, key=lambda posto: posto['_key'], reverse=False) if not target_post_id: - target_post_id = postos[0]._key + target_post_id = postos[0]['_key'] return render_template("interactive-posts.html", host_id=docker_short_id, postos=postos, target_post_id=target_post_id) From 1e2d39a5c0f140a051a8d6f4928fe050d0b7c529 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:02:34 +1000 Subject: [PATCH 27/80] check many hexes --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index d641a35..2c84068 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -11,7 +11,7 @@
Создай то, о чем будут говорить многие. - >> + >>
From e7fee4999e53199069fb2238261d8e085e8db050 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:14:13 +1000 Subject: [PATCH 28/80] check many hexes --- app.py | 37 ++++++++++++++++++++++++++++++++ templates/interactive-posts.html | 25 ++++++++------------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/app.py b/app.py index 403739a..d16f393 100644 --- a/app.py +++ b/app.py @@ -102,6 +102,43 @@ def post_a_post(post_key): return render_template("i-posts.html", host_id=docker_short_id, postos=postos) + +@app.route('/post_to_another_post/', methods=['POST']) +def post_a_post(post_key): + + + postos = db.collection('posts') + + data = request.form['send_this_text'] + + metadata = postos.insert({ + 'texto': data, + 'parent_post': f'{post_key}' + }, overwrite_mode='update') + + metadata = db.collection('post_parents').insert({ + '_from': f'posts/{post_key}', + '_to': f'posts/{metadata["_key"]}' + }, overwrite_mode='update') + + + cursor = db.aql.execute( + """FOR ppp IN posts + LET children = (FOR v IN 1..9999 OUTBOUND ppp post_parents RETURN v) + UPDATE ppp WITH { children_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" + ) + cursor = db.aql.execute( + """FOR ppp IN posts + LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + UPDATE ppp WITH { answers_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" + ) + + postos = db.collection('posts') + postos = sorted(postos, key=lambda posto: posto['_key'], reverse=False) + + return render_template("i-posts.html", host_id=docker_short_id, postos=postos) + + # @app.route('/post_my_file', method=['POST']) # def post_a_file(): # pass diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 2c84068..ad5832f 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -18,24 +18,17 @@
-
- -
- +
\ No newline at end of file diff --git a/templates/i-posts.html b/templates/i-posts.html index 474ac04..20594c9 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -33,7 +33,8 @@
diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 1a21115..3eca4a2 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,39 +6,8 @@ {% include 'navbar.html' %}
-
-
-
-
- Создай то, о чем будут говорить многие. - >> -
- - -
-
-
-
- -
- - -
-
+
+ {% include 'answer-post.html' %}
From 78a60c6d90858899f8d030c29a896cd9e143cedf Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:29:33 +1000 Subject: [PATCH 33/80] we can chat now? --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 6a9b4f4..484711a 100644 --- a/app.py +++ b/app.py @@ -65,7 +65,7 @@ def page_posts_from_db(target_post_id=None): @app.route("/answer_post/") -def page_posts_from_db(target_post_id): +def answer_post(target_post_id): postos = db.collection('posts') postos = [ p for p in postos] From a622768a1317cc14126f352955ed80c2aa3b6262 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:32:18 +1000 Subject: [PATCH 34/80] we can chat now? --- templates/interactive-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 3eca4a2..f249d53 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,7 +6,7 @@ {% include 'navbar.html' %}
-
+
{% include 'answer-post.html' %}
From d97035a32b30a74526b1c12cabb038958894a637 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:34:30 +1000 Subject: [PATCH 35/80] we can chat now? --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 484711a..07f9e75 100644 --- a/app.py +++ b/app.py @@ -64,7 +64,7 @@ def page_posts_from_db(target_post_id=None): return render_template("interactive-posts.html", host_id=docker_short_id, postos=postos, target_post_id=target_post_id) -@app.route("/answer_post/") +@app.route("/answer_post/", methods=['POST']) def answer_post(target_post_id): postos = db.collection('posts') From fc5b0d7491242045a1101dc64bb526fb27754ff9 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:50:31 +1000 Subject: [PATCH 36/80] we can chat now? --- templates/answer-post.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/answer-post.html b/templates/answer-post.html index d9d31af..2c46f3e 100644 --- a/templates/answer-post.html +++ b/templates/answer-post.html @@ -1,7 +1,7 @@
- Создай то, о чем будут говорить многие. + Создай то, о чем будут говорить многие. Ты отвечаешь: >>{{ posto.parent_post }} >>
From b3913268062c4e2f13ac2230a3bb89594f7ed258 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:53:20 +1000 Subject: [PATCH 37/80] we can chat now? --- templates/answer-post.html | 2 +- templates/interactive-posts.html | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/answer-post.html b/templates/answer-post.html index 2c46f3e..30f7e3f 100644 --- a/templates/answer-post.html +++ b/templates/answer-post.html @@ -1,7 +1,7 @@
- Создай то, о чем будут говорить многие. Ты отвечаешь: >>{{ posto.parent_post }} + Создай то, о чем будут говорить многие. Ты отвечаешь: >>{{ target_post_id }} >>
diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index f249d53..82dd12e 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -5,12 +5,6 @@ {% include 'navbar.html' %} -
-
- {% include 'answer-post.html' %} -
-
-
{% include 'i-posts.html' %} @@ -18,4 +12,10 @@
+
+
+ {% include 'answer-post.html' %} +
+
+ {% endblock content %} \ No newline at end of file From c30e2c8705e30debe6312a425cb8f9d504660faa Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 00:54:27 +1000 Subject: [PATCH 38/80] we can chat now? --- templates/answer-post.html | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/templates/answer-post.html b/templates/answer-post.html index 30f7e3f..7dd63af 100644 --- a/templates/answer-post.html +++ b/templates/answer-post.html @@ -10,15 +10,17 @@
- + + +
Ответить этому.
+ {% if posto.answers_list | length > 0 %} + {% for ans in posto.answers_list %} + + {% endfor %} + {% endif %}
From 390974f1a3468851672833a544a2e0e0759cbba0 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:03:05 +1000 Subject: [PATCH 40/80] we can chat now? --- templates/i-posts.html | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index a276d84..cedbed9 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -29,6 +29,16 @@ >>{{ posto.parent_post }} {% endif %}

{{ posto.texto }}

+ + {% if posto.answers_list | length > 0 %} +
+

Ответы:

+ {% for ans in posto.answers_list %} + + {% endfor %} + {% endif %}
@@ -36,13 +46,6 @@ Ответить этому.
- {% if posto.answers_list | length > 0 %} - {% for ans in posto.answers_list %} - - {% endfor %} - {% endif %}
From f49f97a8ea5632cf0d4b47b79e5a9973845da7fe Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:04:55 +1000 Subject: [PATCH 41/80] we can chat now? --- templates/i-posts.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index cedbed9..e89bf11 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -33,11 +33,11 @@ {% if posto.answers_list | length > 0 %}

Ответы:

- {% for ans in posto.answers_list %} - +
+ {% for ans in posto.answers_list %} + >>{{ posto.ans }} {% endfor %} +
{% endif %}
From 57e598814f8f432c407a3c71785fe2d3897a96d7 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:05:40 +1000 Subject: [PATCH 42/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index e89bf11..f8a23fd 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% for ans in posto.answers_list %} - >>{{ posto.ans }} + >>{{ ans }} {% endfor %}
{% endif %} From ce5f33e60421142379d58e054b32bcd2b71b6b5a Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:07:42 +1000 Subject: [PATCH 43/80] we can chat now? --- app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app.py b/app.py index e9534bc..210d7b4 100644 --- a/app.py +++ b/app.py @@ -154,6 +154,11 @@ def post_to_post(post_key): LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) UPDATE ppp WITH { answers_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" ) + cursor = db.aql.execute( + """FOR ppp IN posts + LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + UPDATE ppp WITH { answers_list : children[*]._key } IN posts""" + ) postos = db.collection('posts') postos = sorted(postos, key=lambda posto: posto['_key'], reverse=False) From 88e2b021f99515daa05d8da662d08f36c7493e72 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:12:50 +1000 Subject: [PATCH 44/80] we can chat now? --- app.py | 4 ++-- templates/i-posts.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 210d7b4..272c6f7 100644 --- a/app.py +++ b/app.py @@ -115,7 +115,7 @@ def post_a_post(post_key): cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) - UPDATE ppp WITH { answers_list : children[*]._key } IN posts""" + UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num ] } IN posts""" ) postos = db.collection('posts') @@ -157,7 +157,7 @@ def post_to_post(post_key): cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) - UPDATE ppp WITH { answers_list : children[*]._key } IN posts""" + UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num ] } IN posts""" ) postos = db.collection('posts') diff --git a/templates/i-posts.html b/templates/i-posts.html index f8a23fd..66c4ca7 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% for ans in posto.answers_list %} - >>{{ ans }} + >>{{ ans[0] }} {% endfor %}
{% endif %} From 7dce872df339bafd8f3e6f49a9b858341f0c2b8d Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:13:11 +1000 Subject: [PATCH 45/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 66c4ca7..1fca6d5 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% for ans in posto.answers_list %} - >>{{ ans[0] }} + >>{{ ans[0] }} (ответов {{ ans[1] }}) {% endfor %}
{% endif %} From fe439b6b1af9711f982e785cab1ee96ba6fb2fc9 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:14:36 +1000 Subject: [PATCH 46/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 1fca6d5..d177a5d 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -30,7 +30,7 @@ {% endif %}

{{ posto.texto }}

- {% if posto.answers_list | length > 0 %} + {% if posto.answers_list[0] | length > 0 %}

Ответы:

From 694e1dbfcd97062aadcc7541a233b6479016e5ef Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:15:37 +1000 Subject: [PATCH 47/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index d177a5d..e0245da 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% endif %} From 05370dae8b261fc4d3f91e56474e964ca59d72c4 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:16:40 +1000 Subject: [PATCH 48/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index e0245da..679f77c 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% endif %} From 69442b671e3d53fd3467f4047d5acc7e90f420c5 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:17:47 +1000 Subject: [PATCH 49/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 679f77c..ba174cd 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% endif %} From 496ca8524085bbd514998e3083e3ca3d96d1b61f Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:18:59 +1000 Subject: [PATCH 50/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index ba174cd..aafc4e3 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% endif %} From 48ec4c9c59d642c818559bdbb9bfeeb0389e2b14 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:22:07 +1000 Subject: [PATCH 51/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index aafc4e3..25167f9 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -34,7 +34,7 @@

Ответы:

- {% for ans in posto.answers_list %} + {% for ans in posto.answers_list[0] %} >>{{ (posto.answers_list[0])[loop.index] }} (ответов {{ (posto.answers_list[1])[loop.index] }}) {% endfor %}
From 24f294bad10906e25d0bbc8cd0f5076e42f02e51 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:23:02 +1000 Subject: [PATCH 52/80] we can chat now? --- templates/i-posts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 25167f9..a7a1d2d 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,7 @@

Ответы:

{% endif %} From 0b9f37348643618a5bec91d8611d390e88437aa1 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:24:55 +1000 Subject: [PATCH 53/80] we can chat now? --- templates/i-posts.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index a7a1d2d..2bdfe06 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,7 +35,11 @@

Ответы:

{% endif %} From 75f2d5a08f8e3efeef20e4713de4dab44c386b24 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:26:52 +1000 Subject: [PATCH 54/80] we can chat now? --- app.py | 4 ++-- templates/i-posts.html | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 272c6f7..a74fa42 100644 --- a/app.py +++ b/app.py @@ -115,7 +115,7 @@ def post_a_post(post_key): cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) - UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num ] } IN posts""" + UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num, children[*].children_num ] } IN posts""" ) postos = db.collection('posts') @@ -157,7 +157,7 @@ def post_to_post(post_key): cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) - UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num ] } IN posts""" + UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num, children[*].children_num ] } IN posts""" ) postos = db.collection('posts') diff --git a/templates/i-posts.html b/templates/i-posts.html index 2bdfe06..161c288 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -35,8 +35,12 @@

Ответы:

{% for ans in posto.answers_list[0] %} - {% if (posto.answers_list[1])[loop.index-1] > 0 %} - >>{{ (posto.answers_list[0])[loop.index-1] }} (ответов {{ (posto.answers_list[1])[loop.index-1] }}) + {% if (posto.answers_list[1])[loop.index-1] > 0 %} + {% if (posto.answers_list[2])[loop.index-1] > 0 %} + >>{{ (posto.answers_list[0])[loop.index-1] }} (ответов {{ (posto.answers_list[1])[loop.index-1] }}, длина {{ (posto.answers_list[2])[loop.index-1] }}) + {% else %} + >>{{ (posto.answers_list[0])[loop.index-1] }} (ответов {{ (posto.answers_list[1])[loop.index-1] }}) + {% endif %} {% else %} >>{{ (posto.answers_list[0])[loop.index-1] }} {% endif %} From a08e14367b2b82152eaad825380ebdcc208c9a05 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:28:06 +1000 Subject: [PATCH 55/80] we can chat now? --- templates/i-posts.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 161c288..bf2a13f 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -2,9 +2,9 @@ {% for posto in postos %}
{% if posto.root_post %} -
+
{% else %} -
+
{% endif %}
From ae638c25db5356ea2588ab65862de94bf805c5ee Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:29:35 +1000 Subject: [PATCH 56/80] we can chat now? --- templates/i-posts.html | 4 ++-- templates/interactive-posts.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index bf2a13f..189a8cf 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -2,9 +2,9 @@ {% for posto in postos %}
{% if posto.root_post %} -
+
{% else %} -
+
{% endif %}
diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 82dd12e..7a1f465 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -13,7 +13,7 @@
-
+
{% include 'answer-post.html' %}
From a46cc7c78c06717c4f24755435d4a75c9cc98dc0 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:30:56 +1000 Subject: [PATCH 57/80] we can chat now? --- templates/i-posts.html | 4 ++-- templates/interactive-posts.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/i-posts.html b/templates/i-posts.html index 189a8cf..bf2a13f 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -2,9 +2,9 @@ {% for posto in postos %}
{% if posto.root_post %} -
+
{% else %} -
+
{% endif %}
diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 7a1f465..594b1f5 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -6,14 +6,14 @@ {% include 'navbar.html' %}
-
+
{% include 'i-posts.html' %}
-
+
{% include 'answer-post.html' %}
From 67fac37e44bce6fdc88945d5cda57ab285563df3 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:36:02 +1000 Subject: [PATCH 58/80] we can chat now? --- app.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index a74fa42..9a7cb9a 100644 --- a/app.py +++ b/app.py @@ -108,13 +108,16 @@ def post_a_post(post_key): ) cursor = db.aql.execute( """FOR ppp IN posts - LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + LET children = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) UPDATE ppp WITH { answers_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" ) cursor = db.aql.execute( """FOR ppp IN posts - LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + LET children0 = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) + LET children = (FOR c IN children0 + SORT c._key DESC + RETURN c) UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num, children[*].children_num ] } IN posts""" ) @@ -151,12 +154,15 @@ def post_to_post(post_key): ) cursor = db.aql.execute( """FOR ppp IN posts - LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + LET children = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) UPDATE ppp WITH { answers_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" ) cursor = db.aql.execute( """FOR ppp IN posts - LET children = (FOR v IN 1OUTBOUND ppp post_parents RETURN v) + LET children0 = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) + LET children = (FOR c IN children0 + SORT c._key DESC + RETURN c) UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num, children[*].children_num ] } IN posts""" ) From 8d98c04fa3a95966d2ebcea57f853de0f0d04643 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Wed, 19 Jul 2023 01:48:55 +1000 Subject: [PATCH 59/80] we can chat now? --- templates/i-posts.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/i-posts.html b/templates/i-posts.html index bf2a13f..abc29be 100644 --- a/templates/i-posts.html +++ b/templates/i-posts.html @@ -50,6 +50,7 @@
+
Ответить этому. From 5938f4e063353e4adbeeaf1587292a31f1a354bb Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:24:15 +1000 Subject: [PATCH 60/80] we can send now? --- app.py | 33 +++++++- reqs.txt | 3 +- static/src/htmx.js | 130 +++++++++++++++++++++++-------- templates/interactive-posts.html | 7 ++ 4 files changed, 136 insertions(+), 37 deletions(-) diff --git a/app.py b/app.py index 9a7cb9a..c73ea13 100644 --- a/app.py +++ b/app.py @@ -5,8 +5,10 @@ from todo import todos from threads import threads_lists from threads_with_posts import open_threads from arango import ArangoClient +from minio import minioClient + +import socket, os -import socket app = Flask(__name__) @@ -176,7 +178,34 @@ def post_to_post(post_key): # def post_a_file(): # pass - +@app.route("/send_dudes", methods=["POST"]) +def upload_file(): + if request.method == "POST": + uploaded_file = request.files["file"] + if uploaded_file: + bucket_name = "thread-pics" + size = os.fstat(uploaded_file.fileno()).st_size + + minioClient.put_object( + bucket_name, uploaded_file.filename, uploaded_file, size + ) + + return """ +

stored

+
+ + +
+ """ + + return """ +

Upload IPS File

+
+ + +
+ """ + if __name__ == "__main__": diff --git a/reqs.txt b/reqs.txt index 9156824..530e5ee 100644 --- a/reqs.txt +++ b/reqs.txt @@ -1,3 +1,4 @@ flask flask-assets -python-arango \ No newline at end of file +python-arango +minio \ No newline at end of file diff --git a/static/src/htmx.js b/static/src/htmx.js index c7ea3f4..2fb8b9c 100644 --- a/static/src/htmx.js +++ b/static/src/htmx.js @@ -44,6 +44,7 @@ return (function () { defineExtension : defineExtension, removeExtension : removeExtension, logAll : logAll, + logNone : logNone, logger : null, config : { historyEnabled:true, @@ -71,6 +72,7 @@ return (function () { defaultFocusScroll: false, getCacheBusterParam: false, globalViewTransitions: false, + methodsThatUseUrlParams: ["get"], }, parseInterval:parseInterval, _:internalEval, @@ -82,7 +84,7 @@ return (function () { sock.binaryType = htmx.config.wsBinaryType; return sock; }, - version: "1.9.2" + version: "1.9.3" }; /** @type {import("./htmx").HtmxInternalApi} */ @@ -90,9 +92,11 @@ return (function () { addTriggerHandler: addTriggerHandler, bodyContains: bodyContains, canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, filterValues: filterValues, hasAttribute: hasAttribute, getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, getClosestMatch: getClosestMatch, getExpressionVars: getExpressionVars, getHeaders: getHeaders, @@ -105,6 +109,7 @@ return (function () { mergeObjects: mergeObjects, makeSettleInfo: makeSettleInfo, oobSwap: oobSwap, + querySelectorExt: querySelectorExt, selectAndSwap: selectAndSwap, settleImmediately: settleImmediately, shouldCancel: shouldCancel, @@ -475,6 +480,10 @@ return (function () { } } + function logNone() { + htmx.logger = null + } + function find(eltOrSelector, selector) { if (selector) { return eltOrSelector.querySelector(selector); @@ -902,6 +911,17 @@ return (function () { return hash; } + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (let i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.name, handlerInfo.handler); + } + delete internalData.onHandlers + } + } + function deInitNode(element) { var internalData = getInternalData(element); if (internalData.timeout) { @@ -920,12 +940,7 @@ return (function () { } }); } - if (internalData.onHandlers) { - for (let i = 0; i < internalData.onHandlers.length; i++) { - const handlerInfo = internalData.onHandlers[i]; - element.removeEventListener(handlerInfo.name, handlerInfo.handler); - } - } + deInitOnHandlers(element); } function cleanUpElement(element) { @@ -950,7 +965,7 @@ return (function () { 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(e => e != target); while(newElt && newElt !== target) { if (newElt.nodeType === Node.ELEMENT_NODE) { settleInfo.elts.push(newElt); @@ -995,8 +1010,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) { @@ -1070,12 +1085,12 @@ return (function () { } } - 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(elt, fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); handlePreservedElements(fragment); return swap(swapStyle, elt, target, fragment, settleInfo); } @@ -1299,7 +1314,10 @@ return (function () { 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}))) { + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { handler(elt); } processPolling(elt, handler, spec); @@ -1361,11 +1379,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; @@ -1398,7 +1416,7 @@ return (function () { if (explicitCancel || shouldCancel(evt, elt)) { evt.preventDefault(); } - if (maybeFilterEvent(triggerSpec, evt)) { + if (maybeFilterEvent(triggerSpec, elt, evt)) { return; } var eventData = getInternalData(evt); @@ -1650,6 +1668,9 @@ return (function () { var sseEventSource = getInternalData(sseSourceElt).sseEventSource; var sseListener = function (event) { if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { sseEventSource.removeEventListener(sseEventName, sseListener); return; } @@ -1666,7 +1687,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) }; @@ -1770,7 +1791,7 @@ return (function () { observer.observe(elt); addEventListener(elt, handler, nodeData, triggerSpec); } else if (triggerSpec.trigger === "load") { - if (!maybeFilterEvent(triggerSpec, makeEvent("load", {elt:elt}))) { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) { loadImmediately(elt, handler, nodeData, triggerSpec.delay); } } else if (triggerSpec.pollInterval) { @@ -1820,6 +1841,16 @@ return (function () { return document.querySelector("[hx-boost], [data-hx-boost]"); } + function findHxOnWildcardElements(elt) { + if (!document.evaluate) return [] + + let node = null + const elements = [] + const iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + return elements + } + function findElementsToProcess(elt) { if (elt.querySelectorAll) { var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : ""; @@ -1879,7 +1910,7 @@ return (function () { function processHxOn(elt) { var hxOnValue = getAttributeValue(elt, 'hx-on'); - if (hxOnValue) { + if (hxOnValue && htmx.config.allowEval) { var handlers = {} var lines = hxOnValue.split("\n"); var currentEvent = null; @@ -1903,6 +1934,22 @@ return (function () { } } + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (const attr of elt.attributes) { + const { name, value } = attr + if (name.startsWith("hx-on:") || name.startsWith("data-hx-on:")) { + let eventName = name.slice(name.indexOf(":") + 1) + // if the eventName starts with a colon, prepend "htmx" for shorthand support + if (eventName.startsWith(":")) eventName = "htmx" + eventName + + addHxOnEventHandler(elt, eventName, value) + } + } + } + function initNode(elt) { if (elt.closest && elt.closest(htmx.config.disableSelector)) { return; @@ -1959,6 +2006,9 @@ return (function () { elt = resolveTarget(elt); 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); } //==================================================================== @@ -2196,7 +2246,9 @@ 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, item:cached}); } else { @@ -2925,8 +2977,12 @@ return (function () { 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, @@ -2951,6 +3007,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) @@ -2962,26 +3019,25 @@ return (function () { var splitPath = path.split("#"); var pathNoAnchor = splitPath[0]; var anchor = splitPath[1]; - var finalPathForGet = null; - if (verb === 'get') { - 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); } + xhr.open(verb.toUpperCase(), finalPath, true); xhr.overrideMimeType("text/html"); xhr.withCredentials = requestConfig.withCredentials; xhr.timeout = requestConfig.timeout; @@ -3002,7 +3058,7 @@ return (function () { xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, pathInfo: { requestPath: path, - finalRequestPath: finalPathForGet || path, + finalRequestPath: finalPath, anchor: anchor } }; @@ -3077,7 +3133,8 @@ 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; } @@ -3268,8 +3325,13 @@ return (function () { // safari issue - see https://github.com/microsoft/playwright/issues/5894 } + var selectOverride; + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + 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) && diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index 594b1f5..eccf724 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -16,6 +16,13 @@
{% include 'answer-post.html' %}
+
+

Upload IPS File

+
+ + +
+
{% endblock content %} \ No newline at end of file From 25a99b45499632dc8cef53f51c9f6b128c5a7215 Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:28:34 +1000 Subject: [PATCH 61/80] we can send now? --- app.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index c73ea13..3e1577b 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,7 @@ from todo import todos from threads import threads_lists from threads_with_posts import open_threads from arango import ArangoClient -from minio import minioClient +from minio import Minio import socket, os @@ -182,6 +182,10 @@ def post_to_post(post_key): def upload_file(): if request.method == "POST": uploaded_file = request.files["file"] + + minioClient = Minio( + "static.guaranteedstruggle.host", + ) if uploaded_file: bucket_name = "thread-pics" size = os.fstat(uploaded_file.fileno()).st_size From 2f630ddde0e801246fa10748991a848a33ef3f6d Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:32:57 +1000 Subject: [PATCH 62/80] we can send now? --- app.py | 16 +- static/src/_hyperscript.js | 7620 ++++++++++++++++++++++++++++++ templates/interactive-posts.html | 15 +- 3 files changed, 7631 insertions(+), 20 deletions(-) create mode 100644 static/src/_hyperscript.js diff --git a/app.py b/app.py index 3e1577b..a47abe2 100644 --- a/app.py +++ b/app.py @@ -194,21 +194,9 @@ def upload_file(): bucket_name, uploaded_file.filename, uploaded_file, size ) - return """ -

stored

-
- - -
- """ + return "OK" - return """ -

Upload IPS File

-
- - -
- """ + return " NE OK " diff --git a/static/src/_hyperscript.js b/static/src/_hyperscript.js new file mode 100644 index 0000000..a41b6be --- /dev/null +++ b/static/src/_hyperscript.js @@ -0,0 +1,7620 @@ +/** + * @typedef {Object} Hyperscript + */ + +(function (self, factory) { + const _hyperscript = factory(self) + + if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') { + module.exports = _hyperscript + } else { + self['_hyperscript'] = _hyperscript + if ('document' in self) self['_hyperscript'].browserInit() + } +})(typeof self !== 'undefined' ? self : this, (globalScope) => { + + 'use strict'; + + /** + * @type {Object} + * @property {DynamicConverter[]} dynamicResolvers + * + * @callback DynamicConverter + * @param {String} str + * @param {*} value + * @returns {*} + */ + const conversions = { + dynamicResolvers: [ + function(str, value){ + if (str === "Fixed") { + return Number(value).toFixed(); + } else if (str.indexOf("Fixed:") === 0) { + let num = str.split(":")[1]; + return Number(value).toFixed(parseInt(num)); + } + } + ], + String: function (val) { + if (val.toString) { + return val.toString(); + } else { + return "" + val; + } + }, + Int: function (val) { + return parseInt(val); + }, + Float: function (val) { + return parseFloat(val); + }, + Number: function (val) { + return Number(val); + }, + Date: function (val) { + return new Date(val); + }, + Array: function (val) { + return Array.from(val); + }, + JSON: function (val) { + return JSON.stringify(val); + }, + Object: function (val) { + if (val instanceof String) { + val = val.toString(); + } + if (typeof val === "string") { + return JSON.parse(val); + } else { + return Object.assign({}, val); + } + }, + } + + const config = { + attributes: "_, script, data-script", + defaultTransition: "all 500ms ease-in", + disableSelector: "[disable-scripting], [data-disable-scripting]", + hideShowStrategies: {}, + conversions, + } + + class Lexer { + static OP_TABLE = { + "+": "PLUS", + "-": "MINUS", + "*": "MULTIPLY", + "/": "DIVIDE", + ".": "PERIOD", + "..": "ELLIPSIS", + "\\": "BACKSLASH", + ":": "COLON", + "%": "PERCENT", + "|": "PIPE", + "!": "EXCLAMATION", + "?": "QUESTION", + "#": "POUND", + "&": "AMPERSAND", + $: "DOLLAR", + ";": "SEMI", + ",": "COMMA", + "(": "L_PAREN", + ")": "R_PAREN", + "<": "L_ANG", + ">": "R_ANG", + "<=": "LTE_ANG", + ">=": "GTE_ANG", + "==": "EQ", + "===": "EQQ", + "!=": "NEQ", + "!==": "NEQQ", + "{": "L_BRACE", + "}": "R_BRACE", + "[": "L_BRACKET", + "]": "R_BRACKET", + "=": "EQUALS", + }; + + /** + * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class. + * @param {string} c + * @returns boolean + */ + static isValidCSSClassChar(c) { + return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":"; + } + + /** + * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID + * @param {string} c + * @returns boolean + */ + static isValidCSSIDChar(c) { + return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":"; + } + + /** + * isWhitespace returns `true` if the provided character is whitespace. + * @param {string} c + * @returns boolean + */ + static isWhitespace(c) { + return c === " " || c === "\t" || Lexer.isNewline(c); + } + + /** + * positionString returns a string representation of a Token's line and column details. + * @param {Token} token + * @returns string + */ + static positionString(token) { + return "[Line: " + token.line + ", Column: " + token.column + "]"; + } + + /** + * isNewline returns `true` if the provided character is a carrage return or newline + * @param {string} c + * @returns boolean + */ + static isNewline(c) { + return c === "\r" || c === "\n"; + } + + /** + * isNumeric returns `true` if the provided character is a number (0-9) + * @param {string} c + * @returns boolean + */ + static isNumeric(c) { + return c >= "0" && c <= "9"; + } + + /** + * isAlpha returns `true` if the provided character is a letter in the alphabet + * @param {string} c + * @returns boolean + */ + static isAlpha(c) { + return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z"); + } + + /** + * @param {string} c + * @param {boolean} [dollarIsOp] + * @returns boolean + */ + static isIdentifierChar(c, dollarIsOp) { + return c === "_" || c === "$"; + } + + /** + * @param {string} c + * @returns boolean + */ + static isReservedChar(c) { + return c === "`" || c === "^"; + } + + /** + * @param {Token[]} tokens + * @returns {boolean} + */ + static isValidSingleQuoteStringStart(tokens) { + if (tokens.length > 0) { + var previousToken = tokens[tokens.length - 1]; + if ( + previousToken.type === "IDENTIFIER" || + previousToken.type === "CLASS_REF" || + previousToken.type === "ID_REF" + ) { + return false; + } + if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) { + return false; + } + } + return true; + } + + /** + * @param {string} string + * @param {boolean} [template] + * @returns {Tokens} + */ + static tokenize(string, template) { + var tokens = /** @type {Token[]}*/ []; + var source = string; + var position = 0; + var column = 0; + var line = 1; + var lastToken = ""; + var templateBraceCount = 0; + + function inTemplate() { + return template && templateBraceCount === 0; + } + + while (position < source.length) { + if ((currentChar() === "-" && nextChar() === "-" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "-")) + || (currentChar() === "/" && nextChar() === "/" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "/"))) { + consumeComment(); + } else if (currentChar() === "/" && nextChar() === "*" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "*")) { + consumeCommentMultiline(); + } else { + if (Lexer.isWhitespace(currentChar())) { + tokens.push(consumeWhitespace()); + } else if ( + !possiblePrecedingSymbol() && + currentChar() === "." && + (Lexer.isAlpha(nextChar()) || nextChar() === "{" || nextChar() === "-") + ) { + tokens.push(consumeClassReference()); + } else if ( + !possiblePrecedingSymbol() && + currentChar() === "#" && + (Lexer.isAlpha(nextChar()) || nextChar() === "{") + ) { + tokens.push(consumeIdReference()); + } else if (currentChar() === "[" && nextChar() === "@") { + tokens.push(consumeAttributeReference()); + } else if (currentChar() === "@") { + tokens.push(consumeShortAttributeReference()); + } else if (currentChar() === "*" && Lexer.isAlpha(nextChar())) { + tokens.push(consumeStyleReference()); + } else if (Lexer.isAlpha(currentChar()) || (!inTemplate() && Lexer.isIdentifierChar(currentChar()))) { + tokens.push(consumeIdentifier()); + } else if (Lexer.isNumeric(currentChar())) { + tokens.push(consumeNumber()); + } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) { + tokens.push(consumeString()); + } else if (!inTemplate() && currentChar() === "'") { + if (Lexer.isValidSingleQuoteStringStart(tokens)) { + tokens.push(consumeString()); + } else { + tokens.push(consumeOp()); + } + } else if (Lexer.OP_TABLE[currentChar()]) { + if (lastToken === "$" && currentChar() === "{") { + templateBraceCount++; + } + if (currentChar() === "}") { + templateBraceCount--; + } + tokens.push(consumeOp()); + } else if (inTemplate() || Lexer.isReservedChar(currentChar())) { + tokens.push(makeToken("RESERVED", consumeChar())); + } else { + if (position < source.length) { + throw Error("Unknown token: " + currentChar() + " "); + } + } + } + } + + return new Tokens(tokens, [], source); + + /** + * @param {string} [type] + * @param {string} [value] + * @returns {Token} + */ + function makeOpToken(type, value) { + var token = makeToken(type, value); + token.op = true; + return token; + } + + /** + * @param {string} [type] + * @param {string} [value] + * @returns {Token} + */ + function makeToken(type, value) { + return { + type: type, + value: value || "", + start: position, + end: position + 1, + column: column, + line: line, + }; + } + + function consumeComment() { + while (currentChar() && !Lexer.isNewline(currentChar())) { + consumeChar(); + } + consumeChar(); // Consume newline + } + + function consumeCommentMultiline() { + while (currentChar() && !(currentChar() === '*' && nextChar() === '/')) { + consumeChar(); + } + consumeChar(); // Consume "*/" + consumeChar(); + } + + /** + * @returns Token + */ + function consumeClassReference() { + var classRef = makeToken("CLASS_REF"); + var value = consumeChar(); + if (currentChar() === "{") { + classRef.template = true; + value += consumeChar(); + while (currentChar() && currentChar() !== "}") { + value += consumeChar(); + } + if (currentChar() !== "}") { + throw Error("Unterminated class reference"); + } else { + value += consumeChar(); // consume final curly + } + } else { + while (Lexer.isValidCSSClassChar(currentChar())) { + value += consumeChar(); + } + } + classRef.value = value; + classRef.end = position; + return classRef; + } + + /** + * @returns Token + */ + function consumeAttributeReference() { + var attributeRef = makeToken("ATTRIBUTE_REF"); + var value = consumeChar(); + while (position < source.length && currentChar() !== "]") { + value += consumeChar(); + } + if (currentChar() === "]") { + value += consumeChar(); + } + attributeRef.value = value; + attributeRef.end = position; + return attributeRef; + } + + function consumeShortAttributeReference() { + var attributeRef = makeToken("ATTRIBUTE_REF"); + var value = consumeChar(); + while (Lexer.isValidCSSIDChar(currentChar())) { + value += consumeChar(); + } + if (currentChar() === '=') { + value += consumeChar(); + if (currentChar() === '"' || currentChar() === "'") { + let stringValue = consumeString(); + value += stringValue.value; + } else if(Lexer.isAlpha(currentChar()) || + Lexer.isNumeric(currentChar()) || + Lexer.isIdentifierChar(currentChar())) { + let id = consumeIdentifier(); + value += id.value; + } + } + attributeRef.value = value; + attributeRef.end = position; + return attributeRef; + } + + function consumeStyleReference() { + var styleRef = makeToken("STYLE_REF"); + var value = consumeChar(); + while (Lexer.isAlpha(currentChar()) || currentChar() === "-") { + value += consumeChar(); + } + styleRef.value = value; + styleRef.end = position; + return styleRef; + } + + /** + * @returns Token + */ + function consumeIdReference() { + var idRef = makeToken("ID_REF"); + var value = consumeChar(); + if (currentChar() === "{") { + idRef.template = true; + value += consumeChar(); + while (currentChar() && currentChar() !== "}") { + value += consumeChar(); + } + if (currentChar() !== "}") { + throw Error("Unterminated id reference"); + } else { + consumeChar(); // consume final quote + } + } else { + while (Lexer.isValidCSSIDChar(currentChar())) { + value += consumeChar(); + } + } + idRef.value = value; + idRef.end = position; + return idRef; + } + + /** + * @returns Token + */ + function consumeIdentifier() { + var identifier = makeToken("IDENTIFIER"); + var value = consumeChar(); + while (Lexer.isAlpha(currentChar()) || + Lexer.isNumeric(currentChar()) || + Lexer.isIdentifierChar(currentChar())) { + value += consumeChar(); + } + if (currentChar() === "!" && value === "beep") { + value += consumeChar(); + } + identifier.value = value; + identifier.end = position; + return identifier; + } + + /** + * @returns Token + */ + function consumeNumber() { + var number = makeToken("NUMBER"); + var value = consumeChar(); + + // given possible XXX.YYY(e|E)[-]ZZZ consume XXX + while (Lexer.isNumeric(currentChar())) { + value += consumeChar(); + } + + // consume .YYY + if (currentChar() === "." && Lexer.isNumeric(nextChar())) { + value += consumeChar(); + } + while (Lexer.isNumeric(currentChar())) { + value += consumeChar(); + } + + // consume (e|E)[-] + if (currentChar() === "e" || currentChar() === "E") { + // possible scientific notation, e.g. 1e6 or 1e-6 + if (Lexer.isNumeric(nextChar())) { + // e.g. 1e6 + value += consumeChar(); + } else if (nextChar() === "-") { + // e.g. 1e-6 + value += consumeChar(); + // consume the - as well since otherwise we would stop on the next loop + value += consumeChar(); + } + } + + // consume ZZZ + while (Lexer.isNumeric(currentChar())) { + value += consumeChar(); + } + number.value = value; + number.end = position; + return number; + } + + /** + * @returns Token + */ + function consumeOp() { + var op = makeOpToken(); + var value = consumeChar(); // consume leading char + while (currentChar() && Lexer.OP_TABLE[value + currentChar()]) { + value += consumeChar(); + } + op.type = Lexer.OP_TABLE[value]; + op.value = value; + op.end = position; + return op; + } + + /** + * @returns Token + */ + function consumeString() { + var string = makeToken("STRING"); + var startChar = consumeChar(); // consume leading quote + var value = ""; + while (currentChar() && currentChar() !== startChar) { + if (currentChar() === "\\") { + consumeChar(); // consume escape char and get the next one + let nextChar = consumeChar(); + if (nextChar === "b") { + value += "\b"; + } else if (nextChar === "f") { + value += "\f"; + } else if (nextChar === "n") { + value += "\n"; + } else if (nextChar === "r") { + value += "\r"; + } else if (nextChar === "t") { + value += "\t"; + } else if (nextChar === "v") { + value += "\v"; + } else { + value += nextChar; + } + } else { + value += consumeChar(); + } + } + if (currentChar() !== startChar) { + throw Error("Unterminated string at " + Lexer.positionString(string)); + } else { + consumeChar(); // consume final quote + } + string.value = value; + string.end = position; + string.template = startChar === "`"; + return string; + } + + /** + * @returns string + */ + function currentChar() { + return source.charAt(position); + } + + /** + * @returns string + */ + function nextChar() { + return source.charAt(position + 1); + } + + function nextCharAt(number = 1) { + return source.charAt(position + number); + } + + /** + * @returns string + */ + function consumeChar() { + lastToken = currentChar(); + position++; + column++; + return lastToken; + } + + /** + * @returns boolean + */ + function possiblePrecedingSymbol() { + return ( + Lexer.isAlpha(lastToken) || + Lexer.isNumeric(lastToken) || + lastToken === ")" || + lastToken === "\"" || + lastToken === "'" || + lastToken === "`" || + lastToken === "}" || + lastToken === "]" + ); + } + + /** + * @returns Token + */ + function consumeWhitespace() { + var whitespace = makeToken("WHITESPACE"); + var value = ""; + while (currentChar() && Lexer.isWhitespace(currentChar())) { + if (Lexer.isNewline(currentChar())) { + column = 0; + line++; + } + value += consumeChar(); + } + whitespace.value = value; + whitespace.end = position; + return whitespace; + } + } + + /** + * @param {string} string + * @param {boolean} [template] + * @returns {Tokens} + */ + tokenize(string, template) { + return Lexer.tokenize(string, template) + } + } + + /** + * @typedef {Object} Token + * @property {string} [type] + * @property {string} value + * @property {number} [start] + * @property {number} [end] + * @property {number} [column] + * @property {number} [line] + * @property {boolean} [op] `true` if this token represents an operator + * @property {boolean} [template] `true` if this token is a template, for class refs, id refs, strings + */ + + class Tokens { + constructor(tokens, consumed, source) { + this.tokens = tokens + this.consumed = consumed + this.source = source + + this.consumeWhitespace(); // consume initial whitespace + } + + get list() { + return this.tokens + } + + /** @type Token | null */ + _lastConsumed = null; + + consumeWhitespace() { + while (this.token(0, true).type === "WHITESPACE") { + this.consumed.push(this.tokens.shift()); + } + } + + /** + * @param {Tokens} tokens + * @param {*} error + * @returns {never} + */ + raiseError(tokens, error) { + Parser.raiseParseError(tokens, error); + } + + /** + * @param {string} value + * @returns {Token} + */ + requireOpToken(value) { + var token = this.matchOpToken(value); + if (token) { + return token; + } else { + this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'"); + } + } + + /** + * @param {string} op1 + * @param {string} [op2] + * @param {string} [op3] + * @returns {Token | void} + */ + matchAnyOpToken(op1, op2, op3) { + for (var i = 0; i < arguments.length; i++) { + var opToken = arguments[i]; + var match = this.matchOpToken(opToken); + if (match) { + return match; + } + } + } + + /** + * @param {string} op1 + * @param {string} [op2] + * @param {string} [op3] + * @returns {Token | void} + */ + matchAnyToken(op1, op2, op3) { + for (var i = 0; i < arguments.length; i++) { + var opToken = arguments[i]; + var match = this.matchToken(opToken); + if (match) { + return match; + } + } + } + + /** + * @param {string} value + * @returns {Token | void} + */ + matchOpToken(value) { + if (this.currentToken() && this.currentToken().op && this.currentToken().value === value) { + return this.consumeToken(); + } + } + + /** + * @param {string} type1 + * @param {string} [type2] + * @param {string} [type3] + * @param {string} [type4] + * @returns {Token} + */ + requireTokenType(type1, type2, type3, type4) { + var token = this.matchTokenType(type1, type2, type3, type4); + if (token) { + return token; + } else { + this.raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3])); + } + } + + /** + * @param {string} type1 + * @param {string} [type2] + * @param {string} [type3] + * @param {string} [type4] + * @returns {Token | void} + */ + matchTokenType(type1, type2, type3, type4) { + if ( + this.currentToken() && + this.currentToken().type && + [type1, type2, type3, type4].indexOf(this.currentToken().type) >= 0 + ) { + return this.consumeToken(); + } + } + + /** + * @param {string} value + * @param {string} [type] + * @returns {Token} + */ + requireToken(value, type) { + var token = this.matchToken(value, type); + if (token) { + return token; + } else { + this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'"); + } + } + + peekToken(value, peek, type) { + peek = peek || 0; + type = type || "IDENTIFIER"; + return this.tokens[peek] && this.tokens[peek].value === value && this.tokens[peek].type === type + } + + /** + * @param {string} value + * @param {string} [type] + * @returns {Token | void} + */ + matchToken(value, type) { + if (this.follows.indexOf(value) !== -1) { + return; // disallowed token here + } + type = type || "IDENTIFIER"; + if (this.currentToken() && this.currentToken().value === value && this.currentToken().type === type) { + return this.consumeToken(); + } + } + + /** + * @returns {Token} + */ + consumeToken() { + var match = this.tokens.shift(); + this.consumed.push(match); + this._lastConsumed = match; + this.consumeWhitespace(); // consume any whitespace + return match; + } + + /** + * @param {string | null} value + * @param {string | null} [type] + * @returns {Token[]} + */ + consumeUntil(value, type) { + /** @type Token[] */ + var tokenList = []; + var currentToken = this.token(0, true); + + while ( + (type == null || currentToken.type !== type) && + (value == null || currentToken.value !== value) && + currentToken.type !== "EOF" + ) { + var match = this.tokens.shift(); + this.consumed.push(match); + tokenList.push(currentToken); + currentToken = this.token(0, true); + } + this.consumeWhitespace(); // consume any whitespace + return tokenList; + } + + /** + * @returns {string} + */ + lastWhitespace() { + if (this.consumed[this.consumed.length - 1] && this.consumed[this.consumed.length - 1].type === "WHITESPACE") { + return this.consumed[this.consumed.length - 1].value; + } else { + return ""; + } + } + + consumeUntilWhitespace() { + return this.consumeUntil(null, "WHITESPACE"); + } + + /** + * @returns {boolean} + */ + hasMore() { + return this.tokens.length > 0; + } + + /** + * @param {number} n + * @param {boolean} [dontIgnoreWhitespace] + * @returns {Token} + */ + token(n, dontIgnoreWhitespace) { + var /**@type {Token}*/ token; + var i = 0; + do { + if (!dontIgnoreWhitespace) { + while (this.tokens[i] && this.tokens[i].type === "WHITESPACE") { + i++; + } + } + token = this.tokens[i]; + n--; + i++; + } while (n > -1); + if (token) { + return token; + } else { + return { + type: "EOF", + value: "<<>>", + }; + } + } + + /** + * @returns {Token} + */ + currentToken() { + return this.token(0); + } + + /** + * @returns {Token | null} + */ + lastMatch() { + return this._lastConsumed; + } + + /** + * @returns {string} + */ + static sourceFor = function () { + return this.programSource.substring(this.startToken.start, this.endToken.end); + } + + /** + * @returns {string} + */ + static lineFor = function () { + return this.programSource.split("\n")[this.startToken.line - 1]; + } + + follows = []; + + pushFollow(str) { + this.follows.push(str); + } + + popFollow() { + this.follows.pop(); + } + + clearFollows() { + var tmp = this.follows; + this.follows = []; + return tmp; + } + + restoreFollows(f) { + this.follows = f; + } + } + + /** + * @callback ParseRule + * @param {Parser} parser + * @param {Runtime} runtime + * @param {Tokens} tokens + * @param {*} [root] + * @returns {ASTNode | undefined} + * + * @typedef {Object} ASTNode + * @member {boolean} isFeature + * @member {string} type + * @member {any[]} args + * @member {(this: ASTNode, ctx:Context, root:any, ...args:any) => any} op + * @member {(this: ASTNode, context?:Context) => any} evaluate + * @member {ASTNode} parent + * @member {Set} children + * @member {ASTNode} root + * @member {String} keyword + * @member {Token} endToken + * @member {ASTNode} next + * @member {(context:Context) => ASTNode} resolveNext + * @member {EventSource} eventSource + * @member {(this: ASTNode) => void} install + * @member {(this: ASTNode, context:Context) => void} execute + * @member {(this: ASTNode, target: object, source: object, args?: Object) => void} apply + * + * + */ + + class Parser { + /** + * + * @param {Runtime} runtime + */ + constructor(runtime) { + this.runtime = runtime + + this.possessivesDisabled = false + + /* ============================================================================================ */ + /* Core hyperscript Grammar Elements */ + /* ============================================================================================ */ + this.addGrammarElement("feature", function (parser, runtime, tokens) { + if (tokens.matchOpToken("(")) { + var featureElement = parser.requireElement("feature", tokens); + tokens.requireOpToken(")"); + return featureElement; + } + + var featureDefinition = parser.FEATURES[tokens.currentToken().value || ""]; + if (featureDefinition) { + return featureDefinition(parser, runtime, tokens); + } + }); + + this.addGrammarElement("command", function (parser, runtime, tokens) { + if (tokens.matchOpToken("(")) { + const commandElement = parser.requireElement("command", tokens); + tokens.requireOpToken(")"); + return commandElement; + } + + var commandDefinition = parser.COMMANDS[tokens.currentToken().value || ""]; + let commandElement; + if (commandDefinition) { + commandElement = commandDefinition(parser, runtime, tokens); + } else if (tokens.currentToken().type === "IDENTIFIER") { + commandElement = parser.parseElement("pseudoCommand", tokens); + } + if (commandElement) { + return parser.parseElement("indirectStatement", tokens, commandElement); + } + + return commandElement; + }); + + this.addGrammarElement("commandList", function (parser, runtime, tokens) { + if (tokens.hasMore()) { + var cmd = parser.parseElement("command", tokens); + if (cmd) { + tokens.matchToken("then"); + const next = parser.parseElement("commandList", tokens); + if (next) cmd.next = next; + return cmd; + } + } + return { + type: "emptyCommandListCommand", + op: function(context){ + return runtime.findNext(this, context); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + } + } + }); + + this.addGrammarElement("leaf", function (parser, runtime, tokens) { + var result = parser.parseAnyOf(parser.LEAF_EXPRESSIONS, tokens); + // symbol is last so it doesn't consume any constants + if (result == null) { + return parser.parseElement("symbol", tokens); + } + + return result; + }); + + this.addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) { + for (var i = 0; i < parser.INDIRECT_EXPRESSIONS.length; i++) { + var indirect = parser.INDIRECT_EXPRESSIONS[i]; + root.endToken = tokens.lastMatch(); + var result = parser.parseElement(indirect, tokens, root); + if (result) { + return result; + } + } + return root; + }); + + this.addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) { + if (tokens.matchToken("unless")) { + root.endToken = tokens.lastMatch(); + var conditional = parser.requireElement("expression", tokens); + var unless = { + type: "unlessStatementModifier", + args: [conditional], + op: function (context, conditional) { + if (conditional) { + return this.next; + } else { + return root; + } + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + root.parent = unless; + return unless; + } + return root; + }); + + this.addGrammarElement("primaryExpression", function (parser, runtime, tokens) { + var leaf = parser.parseElement("leaf", tokens); + if (leaf) { + return parser.parseElement("indirectExpression", tokens, leaf); + } + parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value); + }); + } + + use(plugin) { + plugin(this) + return this + } + + /** @type {Object} */ + GRAMMAR = {}; + + /** @type {Object} */ + COMMANDS = {}; + + /** @type {Object} */ + FEATURES = {}; + + /** @type {string[]} */ + LEAF_EXPRESSIONS = []; + /** @type {string[]} */ + INDIRECT_EXPRESSIONS = []; + + /** + * @param {*} parseElement + * @param {*} start + * @param {Tokens} tokens + */ + initElt(parseElement, start, tokens) { + parseElement.startToken = start; + parseElement.sourceFor = Tokens.sourceFor; + parseElement.lineFor = Tokens.lineFor; + parseElement.programSource = tokens.source; + } + + /** + * @param {string} type + * @param {Tokens} tokens + * @param {ASTNode?} root + * @returns {ASTNode} + */ + parseElement(type, tokens, root = undefined) { + var elementDefinition = this.GRAMMAR[type]; + if (elementDefinition) { + var start = tokens.currentToken(); + var parseElement = elementDefinition(this, this.runtime, tokens, root); + if (parseElement) { + this.initElt(parseElement, start, tokens); + parseElement.endToken = parseElement.endToken || tokens.lastMatch(); + var root = parseElement.root; + while (root != null) { + this.initElt(root, start, tokens); + root = root.root; + } + } + return parseElement; + } + } + + /** + * @param {string} type + * @param {Tokens} tokens + * @param {string} [message] + * @param {*} [root] + * @returns {ASTNode} + */ + requireElement(type, tokens, message, root) { + var result = this.parseElement(type, tokens, root); + if (!result) Parser.raiseParseError(tokens, message || "Expected " + type); + // @ts-ignore + return result; + } + + /** + * @param {string[]} types + * @param {Tokens} tokens + * @returns {ASTNode} + */ + parseAnyOf(types, tokens) { + for (var i = 0; i < types.length; i++) { + var type = types[i]; + var expression = this.parseElement(type, tokens); + if (expression) { + return expression; + } + } + } + + /** + * @param {string} name + * @param {ParseRule} definition + */ + addGrammarElement(name, definition) { + this.GRAMMAR[name] = definition; + } + + /** + * @param {string} keyword + * @param {ParseRule} definition + */ + addCommand(keyword, definition) { + var commandGrammarType = keyword + "Command"; + var commandDefinitionWrapper = function (parser, runtime, tokens) { + const commandElement = definition(parser, runtime, tokens); + if (commandElement) { + commandElement.type = commandGrammarType; + commandElement.execute = function (context) { + context.meta.command = commandElement; + return runtime.unifiedExec(this, context); + }; + return commandElement; + } + }; + this.GRAMMAR[commandGrammarType] = commandDefinitionWrapper; + this.COMMANDS[keyword] = commandDefinitionWrapper; + } + + /** + * @param {string} keyword + * @param {ParseRule} definition + */ + addFeature(keyword, definition) { + var featureGrammarType = keyword + "Feature"; + + /** @type {ParseRule} */ + var featureDefinitionWrapper = function (parser, runtime, tokens) { + var featureElement = definition(parser, runtime, tokens); + if (featureElement) { + featureElement.isFeature = true; + featureElement.keyword = keyword; + featureElement.type = featureGrammarType; + return featureElement; + } + }; + this.GRAMMAR[featureGrammarType] = featureDefinitionWrapper; + this.FEATURES[keyword] = featureDefinitionWrapper; + } + + /** + * @param {string} name + * @param {ParseRule} definition + */ + addLeafExpression(name, definition) { + this.LEAF_EXPRESSIONS.push(name); + this.addGrammarElement(name, definition); + } + + /** + * @param {string} name + * @param {ParseRule} definition + */ + addIndirectExpression(name, definition) { + this.INDIRECT_EXPRESSIONS.push(name); + this.addGrammarElement(name, definition); + } + + /** + * + * @param {Tokens} tokens + * @returns string + */ + static createParserContext(tokens) { + var currentToken = tokens.currentToken(); + var source = tokens.source; + var lines = source.split("\n"); + var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1; + var contextLine = lines[line]; + var offset = /** @type {number} */ ( + currentToken && currentToken.line ? currentToken.column : contextLine.length - 1); + return contextLine + "\n" + " ".repeat(offset) + "^^\n\n"; + } + + /** + * @param {Tokens} tokens + * @param {string} [message] + * @returns {never} + */ + static raiseParseError(tokens, message) { + message = + (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + Parser.createParserContext(tokens); + var error = new Error(message); + error["tokens"] = tokens; + throw error; + } + + /** + * @param {Tokens} tokens + * @param {string} [message] + */ + raiseParseError(tokens, message) { + Parser.raiseParseError(tokens, message) + } + + /** + * @param {Tokens} tokens + * @returns {ASTNode} + */ + parseHyperScript(tokens) { + var result = this.parseElement("hyperscript", tokens); + if (tokens.hasMore()) this.raiseParseError(tokens); + if (result) return result; + } + + /** + * @param {ASTNode | undefined} elt + * @param {ASTNode} parent + */ + setParent(elt, parent) { + if (typeof elt === 'object') { + elt.parent = parent; + if (typeof parent === 'object') { + parent.children = (parent.children || new Set()); + parent.children.add(elt) + } + this.setParent(elt.next, parent); + } + } + + /** + * @param {Token} token + * @returns {ParseRule} + */ + commandStart(token) { + return this.COMMANDS[token.value || ""]; + } + + /** + * @param {Token} token + * @returns {ParseRule} + */ + featureStart(token) { + return this.FEATURES[token.value || ""]; + } + + /** + * @param {Token} token + * @returns {boolean} + */ + commandBoundary(token) { + if ( + token.value == "end" || + token.value == "then" || + token.value == "else" || + token.value == "otherwise" || + token.value == ")" || + this.commandStart(token) || + this.featureStart(token) || + token.type == "EOF" + ) { + return true; + } + return false; + } + + /** + * @param {Tokens} tokens + * @returns {(string | ASTNode)[]} + */ + parseStringTemplate(tokens) { + /** @type {(string | ASTNode)[]} */ + var returnArr = [""]; + do { + returnArr.push(tokens.lastWhitespace()); + if (tokens.currentToken().value === "$") { + tokens.consumeToken(); + var startingBrace = tokens.matchOpToken("{"); + returnArr.push(this.requireElement("expression", tokens)); + if (startingBrace) { + tokens.requireOpToken("}"); + } + returnArr.push(""); + } else if (tokens.currentToken().value === "\\") { + tokens.consumeToken(); // skip next + tokens.consumeToken(); + } else { + var token = tokens.consumeToken(); + returnArr[returnArr.length - 1] += token ? token.value : ""; + } + } while (tokens.hasMore()); + returnArr.push(tokens.lastWhitespace()); + return returnArr; + } + + /** + * @param {ASTNode} commandList + */ + ensureTerminated(commandList) { + const runtime = this.runtime + var implicitReturn = { + type: "implicitReturn", + op: function (context) { + context.meta.returned = true; + if (context.meta.resolve) { + context.meta.resolve(); + } + return runtime.HALT; + }, + execute: function (ctx) { + // do nothing + }, + }; + + var end = commandList; + while (end.next) { + end = end.next; + } + end.next = implicitReturn; + } + } + + class Runtime { + /** + * + * @param {Lexer} [lexer] + * @param {Parser} [parser] + */ + constructor(lexer, parser) { + this.lexer = lexer ?? new Lexer; + this.parser = parser ?? new Parser(this) + .use(hyperscriptCoreGrammar) + .use(hyperscriptWebGrammar); + this.parser.runtime = this + } + + /** + * @param {HTMLElement} elt + * @param {string} selector + * @returns boolean + */ + matchesSelector(elt, selector) { + // noinspection JSUnresolvedVariable + var matchesFunction = + // @ts-ignore + elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; + return matchesFunction && matchesFunction.call(elt, selector); + } + + /** + * @param {string} eventName + * @param {Object} [detail] + * @returns {Event} + */ + makeEvent(eventName, detail) { + var evt; + if (globalScope.Event && typeof globalScope.Event === "function") { + evt = new Event(eventName, { + bubbles: true, + cancelable: true, + }); + evt['detail'] = detail; + } else { + evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(eventName, true, true, detail); + } + return evt; + } + + /** + * @param {Element} elt + * @param {string} eventName + * @param {Object} [detail] + * @param {Element} [sender] + * @returns {boolean} + */ + triggerEvent(elt, eventName, detail, sender) { + detail = detail || {}; + detail["sender"] = sender; + var event = this.makeEvent(eventName, detail); + var eventResult = elt.dispatchEvent(event); + return eventResult; + } + + /** + * isArrayLike returns `true` if the provided value is an array or + * a NodeList (which is close enough to being an array for our purposes). + * + * @param {any} value + * @returns {value is Array | NodeList} + */ + isArrayLike(value) { + return Array.isArray(value) || + (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection)); + } + + /** + * isIterable returns `true` if the provided value supports the + * iterator protocol. + * + * @param {any} value + * @returns {value is Iterable} + */ + isIterable(value) { + return typeof value === 'object' + && Symbol.iterator in value + && typeof value[Symbol.iterator] === 'function'; + } + + /** + * shouldAutoIterate returns `true` if the provided value + * should be implicitly iterated over when accessing properties, + * and as the target of some commands. + * + * Currently, this is when the value is an {ElementCollection} + * or {isArrayLike} returns true. + * + * @param {any} value + * @returns {value is (any[] | ElementCollection)} + */ + shouldAutoIterate(value) { + return value != null && value[shouldAutoIterateSymbol] || + this.isArrayLike(value); + } + + /** + * forEach executes the provided `func` on every item in the `value` array. + * if `value` is a single item (and not an array) then `func` is simply called + * once. If `value` is null, then no further actions are taken. + * + * @template T + * @param {T | Iterable} value + * @param {(item: T) => void} func + */ + forEach(value, func) { + if (value == null) { + // do nothing + } else if (this.isIterable(value)) { + for (const nth of value) { + func(nth); + } + } else if (this.isArrayLike(value)) { + for (var i = 0; i < value.length; i++) { + func(value[i]); + } + } else { + func(value); + } + } + + /** + * implicitLoop executes the provided `func` on: + * - every item of {value}, if {value} should be auto-iterated + * (see {shouldAutoIterate}) + * - {value} otherwise + * + * @template T + * @param {ElementCollection | T | T[]} value + * @param {(item: T) => void} func + */ + implicitLoop(value, func) { + if (this.shouldAutoIterate(value)) { + for (const x of value) func(x); + } else { + func(value); + } + } + + wrapArrays(args) { + var arr = []; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (Array.isArray(arg)) { + arr.push(Promise.all(arg)); + } else { + arr.push(arg); + } + } + return arr; + } + + unwrapAsyncs(values) { + for (var i = 0; i < values.length; i++) { + var value = values[i]; + if (value.asyncWrapper) { + values[i] = value.value; + } + if (Array.isArray(value)) { + for (var j = 0; j < value.length; j++) { + var valueElement = value[j]; + if (valueElement.asyncWrapper) { + value[j] = valueElement.value; + } + } + } + } + } + + static HALT = {}; + HALT = Runtime.HALT; + + /** + * @param {ASTNode} command + * @param {Context} ctx + */ + unifiedExec(command, ctx) { + while (true) { + try { + var next = this.unifiedEval(command, ctx); + } catch (e) { + if (ctx.meta.handlingFinally) { + console.error(" Exception in finally block: ", e); + next = Runtime.HALT; + } else { + this.registerHyperTrace(ctx, e); + if (ctx.meta.errorHandler && !ctx.meta.handlingError) { + ctx.meta.handlingError = true; + ctx.locals[ctx.meta.errorSymbol] = e; + command = ctx.meta.errorHandler; + continue; + } else { + ctx.meta.currentException = e; + next = Runtime.HALT; + } + } + } + if (next == null) { + console.error(command, " did not return a next element to execute! context: ", ctx); + return; + } else if (next.then) { + next.then(resolvedNext => { + this.unifiedExec(resolvedNext, ctx); + }).catch(reason => { + this.unifiedExec({ // Anonymous command to simply throw the exception + op: function(){ + throw reason; + } + }, ctx); + }); + return; + } else if (next === Runtime.HALT) { + if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) { + ctx.meta.handlingFinally = true; + command = ctx.meta.finallyHandler; + } else { + if (ctx.meta.onHalt) { + ctx.meta.onHalt(); + } + if (ctx.meta.currentException) { + if (ctx.meta.reject) { + ctx.meta.reject(ctx.meta.currentException); + return; + } else { + throw ctx.meta.currentException; + } + } else { + return; + } + } + } else { + command = next; // move to the next command + } + } + } + + /** + * @param {*} parseElement + * @param {Context} ctx + * @returns {*} + */ + unifiedEval(parseElement, ctx) { + /** @type any[] */ + var args = [ctx]; + var async = false; + var wrappedAsyncs = false; + + if (parseElement.args) { + for (var i = 0; i < parseElement.args.length; i++) { + var argument = parseElement.args[i]; + if (argument == null) { + args.push(null); + } else if (Array.isArray(argument)) { + var arr = []; + for (var j = 0; j < argument.length; j++) { + var element = argument[j]; + var value = element ? element.evaluate(ctx) : null; // OK + if (value) { + if (value.then) { + async = true; + } else if (value.asyncWrapper) { + wrappedAsyncs = true; + } + } + arr.push(value); + } + args.push(arr); + } else if (argument.evaluate) { + var value = argument.evaluate(ctx); // OK + if (value) { + if (value.then) { + async = true; + } else if (value.asyncWrapper) { + wrappedAsyncs = true; + } + } + args.push(value); + } else { + args.push(argument); + } + } + } + if (async) { + return new Promise((resolve, reject) => { + args = this.wrapArrays(args); + Promise.all(args) + .then(function (values) { + if (wrappedAsyncs) { + this.unwrapAsyncs(values); + } + try { + var apply = parseElement.op.apply(parseElement, values); + resolve(apply); + } catch (e) { + reject(e); + } + }) + .catch(function (reason) { + reject(reason); + }); + }); + } else { + if (wrappedAsyncs) { + this.unwrapAsyncs(args); + } + return parseElement.op.apply(parseElement, args); + } + } + + /** + * @type {string[] | null} + */ + _scriptAttrs = null; + + /** + * getAttributes returns the attribute name(s) to use when + * locating hyperscript scripts in a DOM element. If no value + * has been configured, it defaults to config.attributes + * @returns string[] + */ + getScriptAttributes() { + if (this._scriptAttrs == null) { + this._scriptAttrs = config.attributes.replace(/ /g, "").split(","); + } + return this._scriptAttrs; + } + + /** + * @param {Element} elt + * @returns {string | null} + */ + getScript(elt) { + for (var i = 0; i < this.getScriptAttributes().length; i++) { + var scriptAttribute = this.getScriptAttributes()[i]; + if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) { + return elt.getAttribute(scriptAttribute); + } + } + if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") { + return elt.innerText; + } + return null; + } + + hyperscriptFeaturesMap = new WeakMap + + /** + * @param {*} elt + * @returns {Object} + */ + getHyperscriptFeatures(elt) { + var hyperscriptFeatures = this.hyperscriptFeaturesMap.get(elt); + if (typeof hyperscriptFeatures === 'undefined') { + if (elt) { + // in some rare cases, elt is null and this line crashes + this.hyperscriptFeaturesMap.set(elt, hyperscriptFeatures = {}); + } + } + return hyperscriptFeatures; + } + + /** + * @param {Object} owner + * @param {Context} ctx + */ + addFeatures(owner, ctx) { + if (owner) { + Object.assign(ctx.locals, this.getHyperscriptFeatures(owner)); + this.addFeatures(owner.parentElement, ctx); + } + } + + /** + * @param {*} owner + * @param {*} feature + * @param {*} hyperscriptTarget + * @param {*} event + * @returns {Context} + */ + makeContext(owner, feature, hyperscriptTarget, event) { + return new Context(owner, feature, hyperscriptTarget, event, this) + } + + /** + * @returns string + */ + getScriptSelector() { + return this.getScriptAttributes() + .map(function (attribute) { + return "[" + attribute + "]"; + }) + .join(", "); + } + + /** + * @param {any} value + * @param {string} type + * @returns {any} + */ + convertValue(value, type) { + var dynamicResolvers = conversions.dynamicResolvers; + for (var i = 0; i < dynamicResolvers.length; i++) { + var dynamicResolver = dynamicResolvers[i]; + var converted = dynamicResolver(type, value); + if (converted !== undefined) { + return converted; + } + } + + if (value == null) { + return null; + } + var converter = conversions[type]; + if (converter) { + return converter(value); + } + + throw "Unknown conversion : " + type; + } + + /** + * @param {string} src + * @returns {ASTNode} + */ + parse(src) { + const lexer = this.lexer, parser = this.parser + var tokens = lexer.tokenize(src); + if (this.parser.commandStart(tokens.currentToken())) { + var commandList = parser.requireElement("commandList", tokens); + if (tokens.hasMore()) parser.raiseParseError(tokens); + parser.ensureTerminated(commandList); + return commandList; + } else if (parser.featureStart(tokens.currentToken())) { + var hyperscript = parser.requireElement("hyperscript", tokens); + if (tokens.hasMore()) parser.raiseParseError(tokens); + return hyperscript; + } else { + var expression = parser.requireElement("expression", tokens); + if (tokens.hasMore()) parser.raiseParseError(tokens); + return expression; + } + } + + /** + * + * @param {ASTNode} elt + * @param {Context} ctx + * @returns {any} + */ + evaluateNoPromise(elt, ctx) { + let result = elt.evaluate(ctx); + if (result.next) { + throw new Error(Tokens.sourceFor.call(elt) + " returned a Promise in a context that they are not allowed."); + } + return result; + } + + /** + * @param {string} src + * @param {Partial} [ctx] + * @param {Object} [args] + * @returns {any} + */ + evaluate(src, ctx, args) { + class HyperscriptModule extends EventTarget { + constructor(mod) { + super(); + this.module = mod; + } + toString() { + return this.module.id; + } + } + + var body = 'document' in globalScope + ? globalScope.document.body + : new HyperscriptModule(args && args.module); + ctx = Object.assign(this.makeContext(body, null, body, null), ctx || {}); + var element = this.parse(src); + if (element.execute) { + element.execute(ctx); + return ctx.result; + } else if (element.apply) { + element.apply(body, body, args); + return this.getHyperscriptFeatures(body); + } else { + return element.evaluate(ctx); + } + + function makeModule() { + return {} + } + } + + /** + * @param {HTMLElement} elt + */ + processNode(elt) { + var selector = this.getScriptSelector(); + if (this.matchesSelector(elt, selector)) { + this.initElement(elt, elt); + } + if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") { + this.initElement(elt, document.body); + } + if (elt.querySelectorAll) { + this.forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), elt => { + this.initElement(elt, elt instanceof HTMLScriptElement && elt.type === "text/hyperscript" ? document.body : elt); + }); + } + } + + /** + * @param {Element} elt + * @param {Element} [target] + */ + initElement(elt, target) { + if (elt.closest && elt.closest(config.disableSelector)) { + return; + } + var internalData = this.getInternalData(elt); + if (!internalData.initialized) { + var src = this.getScript(elt); + if (src) { + try { + internalData.initialized = true; + internalData.script = src; + const lexer = this.lexer, parser = this.parser + var tokens = lexer.tokenize(src); + var hyperScript = parser.parseHyperScript(tokens); + if (!hyperScript) return; + hyperScript.apply(target || elt, elt); + setTimeout(() => { + this.triggerEvent(target || elt, "load", { + hyperscript: true, + }); + }, 1); + } catch (e) { + this.triggerEvent(elt, "exception", { + error: e, + }); + console.error( + "hyperscript errors were found on the following element:", + elt, + "\n\n", + e.message, + e.stack + ); + } + } + } + } + + internalDataMap = new WeakMap + + /** + * @param {Element} elt + * @returns {Object} + */ + getInternalData(elt) { + var internalData = this.internalDataMap.get(elt); + if (typeof internalData === 'undefined') { + this.internalDataMap.set(elt, internalData = {}); + } + return internalData; + } + + /** + * @param {any} value + * @param {string} typeString + * @param {boolean} [nullOk] + * @returns {boolean} + */ + typeCheck(value, typeString, nullOk) { + if (value == null && nullOk) { + return true; + } + var typeName = Object.prototype.toString.call(value).slice(8, -1); + return typeName === typeString; + } + + getElementScope(context) { + var elt = context.meta && context.meta.owner; + if (elt) { + var internalData = this.getInternalData(elt); + var scopeName = "elementScope"; + if (context.meta.feature && context.meta.feature.behavior) { + scopeName = context.meta.feature.behavior + "Scope"; + } + var elementScope = getOrInitObject(internalData, scopeName); + return elementScope; + } else { + return {}; // no element, return empty scope + } + } + + /** + * @param {string} str + * @returns {boolean} + */ + isReservedWord(str) { + return ["meta", "it", "result", "locals", "event", "target", "detail", "sender", "body"].includes(str) + } + + /** + * @param {any} context + * @returns {boolean} + */ + isHyperscriptContext(context) { + return context instanceof Context; + } + + /** + * @param {string} str + * @param {Context} context + * @returns {any} + */ + resolveSymbol(str, context, type) { + if (str === "me" || str === "my" || str === "I") { + return context.me; + } + if (str === "it" || str === "its" || str === "result") { + return context.result; + } + if (str === "you" || str === "your" || str === "yourself") { + return context.you; + } else { + if (type === "global") { + return globalScope[str]; + } else if (type === "element") { + var elementScope = this.getElementScope(context); + return elementScope[str]; + } else if (type === "local") { + return context.locals[str]; + } else { + // meta scope (used for event conditionals) + if (context.meta && context.meta.context) { + var fromMetaContext = context.meta.context[str]; + if (typeof fromMetaContext !== "undefined") { + return fromMetaContext; + } + // resolve against the `detail` object in the meta context as well + if (context.meta.context.detail) { + fromMetaContext = context.meta.context.detail[str]; + if (typeof fromMetaContext !== "undefined") { + return fromMetaContext; + } + } + } + if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) { + // local scope + var fromContext = context.locals[str]; + } else { + // direct get from normal JS object or top-level of context + var fromContext = context[str]; + } + if (typeof fromContext !== "undefined") { + return fromContext; + } else { + // element scope + var elementScope = this.getElementScope(context); + fromContext = elementScope[str]; + if (typeof fromContext !== "undefined") { + return fromContext; + } else { + // global scope + return globalScope[str]; + } + } + } + } + } + + setSymbol(str, context, type, value) { + if (type === "global") { + globalScope[str] = value; + } else if (type === "element") { + var elementScope = this.getElementScope(context); + elementScope[str] = value; + } else if (type === "local") { + context.locals[str] = value; + } else { + if (this.isHyperscriptContext(context) && !this.isReservedWord(str) && typeof context.locals[str] !== "undefined") { + // local scope + context.locals[str] = value; + } else { + // element scope + var elementScope = this.getElementScope(context); + var fromContext = elementScope[str]; + if (typeof fromContext !== "undefined") { + elementScope[str] = value; + } else { + if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) { + // local scope + context.locals[str] = value; + } else { + // direct set on normal JS object or top-level of context + context[str] = value; + } + } + } + } + } + + /** + * @param {ASTNode} command + * @param {Context} context + * @returns {undefined | ASTNode} + */ + findNext(command, context) { + if (command) { + if (command.resolveNext) { + return command.resolveNext(context); + } else if (command.next) { + return command.next; + } else { + return this.findNext(command.parent, context); + } + } + } + + /** + * @param {Object} root + * @param {string} property + * @param {Getter} getter + * @returns {any} + * + * @callback Getter + * @param {Object} root + * @param {string} property + */ + flatGet(root, property, getter) { + if (root != null) { + var val = getter(root, property); + if (typeof val !== "undefined") { + return val; + } + + if (this.shouldAutoIterate(root)) { + // flat map + var result = []; + for (var component of root) { + var componentValue = getter(component, property); + result.push(componentValue); + } + return result; + } + } + } + + resolveProperty(root, property) { + return this.flatGet(root, property, (root, property) => root[property] ) + } + + resolveAttribute(root, property) { + return this.flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property) ) + } + + /** + * + * @param {Object} root + * @param {string} property + * @returns {string} + */ + resolveStyle(root, property) { + return this.flatGet(root, property, (root, property) => root.style && root.style[property] ) + } + + /** + * + * @param {Object} root + * @param {string} property + * @returns {string} + */ + resolveComputedStyle(root, property) { + return this.flatGet(root, property, (root, property) => getComputedStyle( + /** @type {Element} */ (root)).getPropertyValue(property) ) + } + + /** + * @param {Element} elt + * @param {string[]} nameSpace + * @param {string} name + * @param {any} value + */ + assignToNamespace(elt, nameSpace, name, value) { + let root + if (typeof document !== "undefined" && elt === document.body) { + root = globalScope; + } else { + root = this.getHyperscriptFeatures(elt); + } + var propertyName; + while ((propertyName = nameSpace.shift()) !== undefined) { + var newRoot = root[propertyName]; + if (newRoot == null) { + newRoot = {}; + root[propertyName] = newRoot; + } + root = newRoot; + } + + root[name] = value; + } + + getHyperTrace(ctx, thrown) { + var trace = []; + var root = ctx; + while (root.meta.caller) { + root = root.meta.caller; + } + if (root.meta.traceMap) { + return root.meta.traceMap.get(thrown, trace); + } + } + + registerHyperTrace(ctx, thrown) { + var trace = []; + var root = null; + while (ctx != null) { + trace.push(ctx); + root = ctx; + ctx = ctx.meta.caller; + } + if (root.meta.traceMap == null) { + root.meta.traceMap = new Map(); // TODO - WeakMap? + } + if (!root.meta.traceMap.get(thrown)) { + var traceEntry = { + trace: trace, + print: function (logger) { + logger = logger || console.error; + logger("hypertrace /// "); + var maxLen = 0; + for (var i = 0; i < trace.length; i++) { + maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length); + } + for (var i = 0; i < trace.length; i++) { + var traceElt = trace[i]; + logger( + " ->", + traceElt.meta.feature.displayName.padEnd(maxLen + 2), + "-", + traceElt.meta.owner + ); + } + }, + }; + root.meta.traceMap.set(thrown, traceEntry); + } + } + + /** + * @param {string} str + * @returns {string} + */ + escapeSelector(str) { + return str.replace(/:/g, function (str) { + return "\\" + str; + }); + } + + /** + * @param {any} value + * @param {*} elt + */ + nullCheck(value, elt) { + if (value == null) { + throw new Error("'" + elt.sourceFor() + "' is null"); + } + } + + /** + * @param {any} value + * @returns {boolean} + */ + isEmpty(value) { + return value == undefined || value.length === 0; + } + + /** + * @param {any} value + * @returns {boolean} + */ + doesExist(value) { + if(value == null){ + return false; + } + if (this.shouldAutoIterate(value)) { + for (const elt of value) { + return true; + } + return false; + } + return true; + } + + /** + * @param {Node} node + * @returns {Document|ShadowRoot} + */ + getRootNode(node) { + if (node && node instanceof Node) { + var rv = node.getRootNode(); + if (rv instanceof Document || rv instanceof ShadowRoot) return rv; + } + return document; + } + + /** + * + * @param {Element} elt + * @param {ASTNode} onFeature + * @returns {EventQueue} + * + * @typedef {{queue:Array, executing:boolean}} EventQueue + */ + getEventQueueFor(elt, onFeature) { + let internalData = this.getInternalData(elt); + var eventQueuesForElt = internalData.eventQueues; + if (eventQueuesForElt == null) { + eventQueuesForElt = new Map(); + internalData.eventQueues = eventQueuesForElt; + } + var eventQueueForFeature = eventQueuesForElt.get(onFeature); + if (eventQueueForFeature == null) { + eventQueueForFeature = {queue:[], executing:false}; + eventQueuesForElt.set(onFeature, eventQueueForFeature); + } + return eventQueueForFeature; + } + + beepValueToConsole(element, expression, value) { + if (this.triggerEvent(element, "hyperscript:beep", {element, expression, value})) { + var typeName; + if (value) { + if (value instanceof ElementCollection) { + typeName = "ElementCollection"; + } else if (value.constructor) { + typeName = value.constructor.name; + } else { + typeName = "unknown"; + } + } else { + typeName = "object (null)" + } + var logValue = value; + if (typeName === "String") { + logValue = '"' + logValue + '"'; + } else if (value instanceof ElementCollection) { + logValue = Array.from(value); + } + console.log("///_ BEEP! The expression (" + Tokens.sourceFor.call(expression).replace("beep! ", "") + ") evaluates to:", logValue, "of type " + typeName); + } + } + + + /** @type string | null */ + // @ts-ignore + hyperscriptUrl = "document" in globalScope && document.currentScript ? document.currentScript.src : null; + } + + + function getCookiesAsArray() { + let cookiesAsArray = document.cookie + .split("; ") + .map(cookieEntry => { + let strings = cookieEntry.split("="); + return {name: strings[0], value: decodeURIComponent(strings[1])} + }); + return cookiesAsArray; + } + + function clearCookie(name) { + document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } + + function clearAllCookies() { + for (const cookie of getCookiesAsArray()) { + clearCookie(cookie.name); + } + } + + const CookieJar = new Proxy({}, { + get(target, prop) { + if (prop === 'then' || prop === 'asyncWrapper') { // ignore special symbols + return null; + } else if (prop === 'length') { + return getCookiesAsArray().length + } else if (prop === 'clear') { + return clearCookie; + } else if (prop === 'clearAll') { + return clearAllCookies; + } else if (typeof prop === "string") { + if (!isNaN(prop)) { + return getCookiesAsArray()[parseInt(prop)]; + + } else { + let value = document.cookie + .split("; ") + .find((row) => row.startsWith(prop + "=")) + ?.split("=")[1]; + if(value) { + return decodeURIComponent(value); + } + } + } else if (prop === Symbol.iterator) { + return getCookiesAsArray()[prop]; + } + }, + set(target, prop, value) { + var finalValue = null; + if ('string' === typeof value) { + finalValue = encodeURIComponent(value) + finalValue += ";samesite=lax" + } else { + finalValue = encodeURIComponent(value.value); + if (value.expires) { + finalValue+=";expires=" + value.maxAge; + } + if (value.maxAge) { + finalValue+=";max-age=" + value.maxAge; + } + if (value.partitioned) { + finalValue+=";partitioned=" + value.partitioned; + } + if (value.path) { + finalValue+=";path=" + value.path; + } + if (value.samesite) { + finalValue+=";samesite=" + value.path; + } + if (value.secure) { + finalValue+=";secure=" + value.path; + } + } + document.cookie= prop + "=" + value; + return true; + } + }) + + class Context { + /** + * @param {*} owner + * @param {*} feature + * @param {*} hyperscriptTarget + * @param {*} event + */ + constructor(owner, feature, hyperscriptTarget, event, runtime) { + this.meta = { + parser: runtime.parser, + lexer: runtime.lexer, + runtime, + owner: owner, + feature: feature, + iterators: {}, + ctx: this + } + this.locals = { + cookies:CookieJar + }; + this.me = hyperscriptTarget, + this.you = undefined + this.result = undefined + this.event = event; + this.target = event ? event.target : null; + this.detail = event ? event.detail : null; + this.sender = event ? event.detail ? event.detail.sender : null : null; + this.body = "document" in globalScope ? document.body : null; + runtime.addFeatures(owner, this); + } + } + + class ElementCollection { + constructor(css, relativeToElement, escape) { + this._css = css; + this.relativeToElement = relativeToElement; + this.escape = escape; + this[shouldAutoIterateSymbol] = true; + } + + get css() { + if (this.escape) { + return Runtime.prototype.escapeSelector(this._css); + } else { + return this._css; + } + } + + get className() { + return this._css.substr(1); + } + + get id() { + return this.className(); + } + + contains(elt) { + for (let element of this) { + if (element.contains(elt)) { + return true; + } + } + return false; + } + + get length() { + return this.selectMatches().length; + } + + [Symbol.iterator]() { + let query = this.selectMatches(); + return query [Symbol.iterator](); + } + + selectMatches() { + let query = Runtime.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css); + return query; + } + } + + const shouldAutoIterateSymbol = Symbol() + + function getOrInitObject(root, prop) { + var value = root[prop]; + if (value) { + return value; + } else { + var newObj = {}; + root[prop] = newObj; + return newObj; + } + } + + /** + * parseJSON parses a JSON string into a corresponding value. If the + * value passed in is not valid JSON, then it logs an error and returns `null`. + * + * @param {string} jString + * @returns any + */ + function parseJSON(jString) { + try { + return JSON.parse(jString); + } catch (error) { + logError(error); + return null; + } + } + + /** + * logError writes an error message to the Javascript console. It can take any + * value, but msg should commonly be a simple string. + * @param {*} msg + */ + function logError(msg) { + if (console.error) { + console.error(msg); + } else if (console.log) { + console.log("ERROR: ", msg); + } + } + + // TODO: JSDoc description of what's happening here + function varargConstructor(Cls, args) { + return new (Cls.bind.apply(Cls, [Cls].concat(args)))(); + } + + // Grammar + + /** + * @param {Parser} parser + */ + function hyperscriptCoreGrammar(parser) { + parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) { + if (tokens.matchOpToken("(")) { + var follows = tokens.clearFollows(); + try { + var expr = parser.requireElement("expression", tokens); + } finally { + tokens.restoreFollows(follows); + } + tokens.requireOpToken(")"); + return expr; + } + }); + + parser.addLeafExpression("string", function (parser, runtime, tokens) { + var stringToken = tokens.matchTokenType("STRING"); + if (!stringToken) return; + var rawValue = /** @type {string} */ (stringToken.value); + /** @type {any[]} */ + var args; + if (stringToken.template) { + var innerTokens = Lexer.tokenize(rawValue, true); + args = parser.parseStringTemplate(innerTokens); + } else { + args = []; + } + return { + type: "string", + token: stringToken, + args: args, + op: function (context) { + var returnStr = ""; + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (val !== undefined) { + returnStr += val; + } + } + return returnStr; + }, + evaluate: function (context) { + if (args.length === 0) { + return rawValue; + } else { + return runtime.unifiedEval(this, context); + } + }, + }; + }); + + parser.addGrammarElement("nakedString", function (parser, runtime, tokens) { + if (tokens.hasMore()) { + var tokenArr = tokens.consumeUntilWhitespace(); + tokens.matchTokenType("WHITESPACE"); + return { + type: "nakedString", + tokens: tokenArr, + evaluate: function (context) { + return tokenArr + .map(function (t) { + return t.value; + }) + .join(""); + }, + }; + } + }); + + parser.addLeafExpression("number", function (parser, runtime, tokens) { + var number = tokens.matchTokenType("NUMBER"); + if (!number) return; + var numberToken = number; + var value = parseFloat(/** @type {string} */ (number.value)); + return { + type: "number", + value: value, + numberToken: numberToken, + evaluate: function () { + return value; + }, + }; + }); + + parser.addLeafExpression("idRef", function (parser, runtime, tokens) { + var elementId = tokens.matchTokenType("ID_REF"); + if (!elementId) return; + if (!elementId.value) return; + // TODO - unify these two expression types + if (elementId.template) { + var templateValue = elementId.value.substring(2); + var innerTokens = Lexer.tokenize(templateValue); + var innerExpression = parser.requireElement("expression", innerTokens); + return { + type: "idRefTemplate", + args: [innerExpression], + op: function (context, arg) { + return runtime.getRootNode(context.me).getElementById(arg); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + const value = elementId.value.substring(1); + return { + type: "idRef", + css: elementId.value, + value: value, + evaluate: function (context) { + return ( + runtime.getRootNode(context.me).getElementById(value) + ); + }, + }; + } + }); + + parser.addLeafExpression("classRef", function (parser, runtime, tokens) { + var classRef = tokens.matchTokenType("CLASS_REF"); + + if (!classRef) return; + if (!classRef.value) return; + + // TODO - unify these two expression types + if (classRef.template) { + var templateValue = classRef.value.substring(2); + var innerTokens = Lexer.tokenize(templateValue); + var innerExpression = parser.requireElement("expression", innerTokens); + return { + type: "classRefTemplate", + args: [innerExpression], + op: function (context, arg) { + return new ElementCollection("." + arg, context.me, true) + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + const css = classRef.value; + return { + type: "classRef", + css: css, + evaluate: function (context) { + return new ElementCollection(css, context.me, true) + }, + }; + } + }); + + class TemplatedQueryElementCollection extends ElementCollection { + constructor(css, relativeToElement, templateParts) { + super(css, relativeToElement); + this.templateParts = templateParts; + this.elements = templateParts.filter(elt => elt instanceof Element); + } + + get css() { + let rv = "", i = 0 + for (const val of this.templateParts) { + if (val instanceof Element) { + rv += "[data-hs-query-id='" + i++ + "']"; + } else rv += val; + } + return rv; + } + + [Symbol.iterator]() { + this.elements.forEach((el, i) => el.dataset.hsQueryId = i); + const rv = super[Symbol.iterator](); + this.elements.forEach(el => el.removeAttribute('data-hs-query-id')); + return rv; + } + } + + parser.addLeafExpression("queryRef", function (parser, runtime, tokens) { + var queryStart = tokens.matchOpToken("<"); + if (!queryStart) return; + var queryTokens = tokens.consumeUntil("/"); + tokens.requireOpToken("/"); + tokens.requireOpToken(">"); + var queryValue = queryTokens + .map(function (t) { + if (t.type === "STRING") { + return '"' + t.value + '"'; + } else { + return t.value; + } + }) + .join(""); + + var template, innerTokens, args; + if (queryValue.indexOf("$") >= 0) { + template = true; + innerTokens = Lexer.tokenize(queryValue, true); + args = parser.parseStringTemplate(innerTokens); + } + + return { + type: "queryRef", + css: queryValue, + args: args, + op: function (context, ...args) { + if (template) { + return new TemplatedQueryElementCollection(queryValue, context.me, args) + } else { + return new ElementCollection(queryValue, context.me) + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) { + var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF"); + if (!attributeRef) return; + if (!attributeRef.value) return; + var outerVal = attributeRef.value; + if (outerVal.indexOf("[") === 0) { + var innerValue = outerVal.substring(2, outerVal.length - 1); + } else { + var innerValue = outerVal.substring(1); + } + var css = "[" + innerValue + "]"; + var split = innerValue.split("="); + var name = split[0]; + var value = split[1]; + if (value) { + // strip quotes + if (value.indexOf('"') === 0) { + value = value.substring(1, value.length - 1); + } + } + return { + type: "attributeRef", + name: name, + css: css, + value: value, + op: function (context) { + var target = context.you || context.me; + if (target) { + return target.getAttribute(name); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addLeafExpression("styleRef", function (parser, runtime, tokens) { + var styleRef = tokens.matchTokenType("STYLE_REF"); + if (!styleRef) return; + if (!styleRef.value) return; + var styleProp = styleRef.value.substr(1); + if (styleProp.startsWith("computed-")) { + styleProp = styleProp.substr("computed-".length); + return { + type: "computedStyleRef", + name: styleProp, + op: function (context) { + var target = context.you || context.me; + if (target) { + return runtime.resolveComputedStyle(target, styleProp); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + return { + type: "styleRef", + name: styleProp, + op: function (context) { + var target = context.you || context.me; + if (target) { + return runtime.resolveStyle(target, styleProp); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + }); + + parser.addGrammarElement("objectKey", function (parser, runtime, tokens) { + var token; + if ((token = tokens.matchTokenType("STRING"))) { + return { + type: "objectKey", + key: token.value, + evaluate: function () { + return token.value; + }, + }; + } else if (tokens.matchOpToken("[")) { + var expr = parser.parseElement("expression", tokens); + tokens.requireOpToken("]"); + return { + type: "objectKey", + expr: expr, + args: [expr], + op: function (ctx, expr) { + return expr; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + var key = ""; + do { + token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-"); + if (token) key += token.value; + } while (token); + return { + type: "objectKey", + key: key, + evaluate: function () { + return key; + }, + }; + } + }); + + parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("{")) return; + var keyExpressions = []; + var valueExpressions = []; + if (!tokens.matchOpToken("}")) { + do { + var name = parser.requireElement("objectKey", tokens); + tokens.requireOpToken(":"); + var value = parser.requireElement("expression", tokens); + valueExpressions.push(value); + keyExpressions.push(name); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken("}"); + } + return { + type: "objectLiteral", + args: [keyExpressions, valueExpressions], + op: function (context, keys, values) { + var returnVal = {}; + for (var i = 0; i < keys.length; i++) { + returnVal[keys[i]] = values[i]; + } + return returnVal; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("nakedNamedArgumentList", function (parser, runtime, tokens) { + var fields = []; + var valueExpressions = []; + if (tokens.currentToken().type === "IDENTIFIER") { + do { + var name = tokens.requireTokenType("IDENTIFIER"); + tokens.requireOpToken(":"); + var value = parser.requireElement("expression", tokens); + valueExpressions.push(value); + fields.push({ name: name, value: value }); + } while (tokens.matchOpToken(",")); + } + return { + type: "namedArgumentList", + fields: fields, + args: [valueExpressions], + op: function (context, values) { + var returnVal = { _namedArgList_: true }; + for (var i = 0; i < values.length; i++) { + var field = fields[i]; + returnVal[field.name.value] = values[i]; + } + return returnVal; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("(")) return; + var elt = parser.requireElement("nakedNamedArgumentList", tokens); + tokens.requireOpToken(")"); + return elt; + }); + + parser.addGrammarElement("symbol", function (parser, runtime, tokens) { + /** @scope {SymbolScope} */ + var scope = "default"; + if (tokens.matchToken("global")) { + scope = "global"; + } else if (tokens.matchToken("element") || tokens.matchToken("module")) { + scope = "element"; + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + } else if (tokens.matchToken("local")) { + scope = "local"; + } + + // TODO better look ahead here + let eltPrefix = tokens.matchOpToken(":"); + let identifier = tokens.matchTokenType("IDENTIFIER"); + if (identifier && identifier.value) { + var name = identifier.value; + if (eltPrefix) { + name = ":" + name; + } + if (scope === "default") { + if (name.indexOf("$") === 0) { + scope = "global"; + } + if (name.indexOf(":") === 0) { + scope = "element"; + } + } + return { + type: "symbol", + token: identifier, + scope: scope, + name: name, + evaluate: function (context) { + return runtime.resolveSymbol(name, context, scope); + }, + }; + } + }); + + parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) { + return { + type: "implicitMeTarget", + evaluate: function (context) { + return context.you || context.me; + }, + }; + }); + + parser.addLeafExpression("boolean", function (parser, runtime, tokens) { + var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false"); + if (!booleanLiteral) return; + const value = booleanLiteral.value === "true"; + return { + type: "boolean", + evaluate: function (context) { + return value; + }, + }; + }); + + parser.addLeafExpression("null", function (parser, runtime, tokens) { + if (tokens.matchToken("null")) { + return { + type: "null", + evaluate: function (context) { + return null; + }, + }; + } + }); + + parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("[")) return; + var values = []; + if (!tokens.matchOpToken("]")) { + do { + var expr = parser.requireElement("expression", tokens); + values.push(expr); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken("]"); + } + return { + type: "arrayLiteral", + values: values, + args: [values], + op: function (context, values) { + return values; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("\\")) return; + var args = []; + var arg1 = tokens.matchTokenType("IDENTIFIER"); + if (arg1) { + args.push(arg1); + while (tokens.matchOpToken(",")) { + args.push(tokens.requireTokenType("IDENTIFIER")); + } + } + // TODO compound op token + tokens.requireOpToken("-"); + tokens.requireOpToken(">"); + var expr = parser.requireElement("expression", tokens); + return { + type: "blockLiteral", + args: args, + expr: expr, + evaluate: function (ctx) { + var returnFunc = function () { + //TODO - push scope + for (var i = 0; i < args.length; i++) { + ctx.locals[args[i].value] = arguments[i]; + } + return expr.evaluate(ctx); //OK + }; + return returnFunc; + }, + }; + }); + + parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken(".")) return; + var prop = tokens.requireTokenType("IDENTIFIER"); + var propertyAccess = { + type: "propertyAccess", + root: root, + prop: prop, + args: [root], + op: function (_context, rootVal) { + var value = runtime.resolveProperty(rootVal, prop.value); + return value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + parser.addIndirectExpression("of", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("of")) return; + var newRoot = parser.requireElement("unaryExpression", tokens); + // find the urroot + var childOfUrRoot = null; + var urRoot = root; + while (urRoot.root) { + childOfUrRoot = urRoot; + urRoot = urRoot.root; + } + if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef" && urRoot.type !== "styleRef" && urRoot.type !== "computedStyleRef") { + parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type); + } + var attribute = urRoot.type === "attributeRef"; + var style = urRoot.type === "styleRef" || urRoot.type === "computedStyleRef"; + if (attribute || style) { + var attributeElt = urRoot + } + var prop = urRoot.name; + + var propertyAccess = { + type: "ofExpression", + prop: urRoot.token, + root: newRoot, + attribute: attributeElt, + expression: root, + args: [newRoot], + op: function (context, rootVal) { + if (attribute) { + return runtime.resolveAttribute(rootVal, prop); + } else if (style) { + if (urRoot.type === "computedStyleRef") { + return runtime.resolveComputedStyle(rootVal, prop); + } else { + return runtime.resolveStyle(rootVal, prop); + } + } else { + return runtime.resolveProperty(rootVal, prop); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + + if (urRoot.type === "attributeRef") { + propertyAccess.attribute = urRoot; + } + if (childOfUrRoot) { + childOfUrRoot.root = propertyAccess; + childOfUrRoot.args = [propertyAccess]; + } else { + root = propertyAccess; + } + + return parser.parseElement("indirectExpression", tokens, root); + }); + + parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) { + if (parser.possessivesDisabled) { + return; + } + var apostrophe = tokens.matchOpToken("'"); + if ( + apostrophe || + (root.type === "symbol" && + (root.name === "my" || root.name === "its" || root.name === "your") && + (tokens.currentToken().type === "IDENTIFIER" || tokens.currentToken().type === "ATTRIBUTE_REF" || tokens.currentToken().type === "STYLE_REF")) + ) { + if (apostrophe) { + tokens.requireToken("s"); + } + + var attribute, style, prop; + attribute = parser.parseElement("attributeRef", tokens); + if (attribute == null) { + style = parser.parseElement("styleRef", tokens); + if (style == null) { + prop = tokens.requireTokenType("IDENTIFIER"); + } + } + var propertyAccess = { + type: "possessive", + root: root, + attribute: attribute || style, + prop: prop, + args: [root], + op: function (context, rootVal) { + if (attribute) { + // @ts-ignore + var value = runtime.resolveAttribute(rootVal, attribute.name); + } else if (style) { + var value + if (style.type === 'computedStyleRef') { + value = runtime.resolveComputedStyle(rootVal, style['name']); + } else { + value = runtime.resolveStyle(rootVal, style['name']); + } + } else { + var value = runtime.resolveProperty(rootVal, prop.value); + } + return value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + } + }); + + parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("in")) return; + var target = parser.requireElement("unaryExpression", tokens); + var propertyAccess = { + type: "inExpression", + root: root, + args: [root, target], + op: function (context, rootVal, target) { + var returnArr = []; + if (rootVal.css) { + runtime.implicitLoop(target, function (targetElt) { + var results = targetElt.querySelectorAll(rootVal.css); + for (var i = 0; i < results.length; i++) { + returnArr.push(results[i]); + } + }); + } else if (rootVal instanceof Element) { + var within = false; + runtime.implicitLoop(target, function (targetElt) { + if (targetElt.contains(rootVal)) { + within = true; + } + }); + if(within) { + return rootVal; + } + } else { + runtime.implicitLoop(rootVal, function (rootElt) { + runtime.implicitLoop(target, function (targetElt) { + if (rootElt === targetElt) { + returnArr.push(rootElt); + } + }); + }); + } + return returnArr; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("as")) return; + tokens.matchToken("a") || tokens.matchToken("an"); + var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise + var propertyAccess = { + type: "asExpression", + root: root, + args: [root], + op: function (context, rootVal) { + return runtime.convertValue(rootVal, conversion); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken("(")) return; + var args = []; + if (!tokens.matchOpToken(")")) { + do { + args.push(parser.requireElement("expression", tokens)); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + + if (root.root) { + var functionCall = { + type: "functionCall", + root: root, + argExressions: args, + args: [root.root, args], + op: function (context, rootRoot, args) { + runtime.nullCheck(rootRoot, root.root); + var func = rootRoot[root.prop.value]; + runtime.nullCheck(func, root); + if (func.hyperfunc) { + args.push(context); + } + return func.apply(rootRoot, args); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + var functionCall = { + type: "functionCall", + root: root, + argExressions: args, + args: [root, args], + op: function (context, func, argVals) { + runtime.nullCheck(func, root); + if (func.hyperfunc) { + argVals.push(context); + } + var apply = func.apply(null, argVals); + return apply; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + return parser.parseElement("indirectExpression", tokens, functionCall); + }); + + parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) { + var attribute = parser.parseElement("attributeRef", tokens); + if (!attribute) return; + var attributeAccess = { + type: "attributeRefAccess", + root: root, + attribute: attribute, + args: [root], + op: function (_ctx, rootVal) { + // @ts-ignore + var value = runtime.resolveAttribute(rootVal, attribute.name); + return value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return attributeAccess; + }); + + parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken("[")) return; + var andBefore = false; + var andAfter = false; + var firstIndex = null; + var secondIndex = null; + + if (tokens.matchOpToken("..")) { + andBefore = true; + firstIndex = parser.requireElement("expression", tokens); + } else { + firstIndex = parser.requireElement("expression", tokens); + + if (tokens.matchOpToken("..")) { + andAfter = true; + var current = tokens.currentToken(); + if (current.type !== "R_BRACKET") { + secondIndex = parser.parseElement("expression", tokens); + } + } + } + tokens.requireOpToken("]"); + + var arrayIndex = { + type: "arrayIndex", + root: root, + prop: firstIndex, + firstIndex: firstIndex, + secondIndex: secondIndex, + args: [root, firstIndex, secondIndex], + op: function (_ctx, root, firstIndex, secondIndex) { + if (root == null) { + return null; + } + if (andBefore) { + if (firstIndex < 0) { + firstIndex = root.length + firstIndex; + } + return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive) + } else if (andAfter) { + if (secondIndex != null) { + if (secondIndex < 0) { + secondIndex = root.length + secondIndex; + } + return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive) + } else { + return root.slice(firstIndex); // returns from firstIndex to end of array + } + } else { + return root[firstIndex]; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + + return parser.parseElement("indirectExpression", tokens, arrayIndex); + }); + + // taken from https://drafts.csswg.org/css-values-4/#relative-length + // and https://drafts.csswg.org/css-values-4/#absolute-length + // (NB: we do not support `in` dues to conflicts w/ the hyperscript grammar) + var STRING_POSTFIXES = [ + 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax', + 'cm', 'mm', 'Q', 'pc', 'pt', 'px' + ]; + parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) { + var root = parser.parseElement("primaryExpression", tokens); + + let stringPosfix = tokens.matchAnyToken.apply(tokens, STRING_POSTFIXES) || tokens.matchOpToken("%"); + if (stringPosfix) { + return { + type: "stringPostfix", + postfix: stringPosfix.value, + args: [root], + op: function (context, val) { + return "" + val + stringPosfix.value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + + var timeFactor = null; + if (tokens.matchToken("s") || tokens.matchToken("seconds")) { + timeFactor = 1000; + } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) { + timeFactor = 1; + } + if (timeFactor) { + return { + type: "timeExpression", + time: root, + factor: timeFactor, + args: [root], + op: function (_context, val) { + return val * timeFactor; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + + if (tokens.matchOpToken(":")) { + var typeName = tokens.requireTokenType("IDENTIFIER"); + if (!typeName.value) return; + var nullOk = !tokens.matchOpToken("!"); + return { + type: "typeCheck", + typeName: typeName, + nullOk: nullOk, + args: [root], + op: function (context, val) { + var passed = runtime.typeCheck(val, this.typeName.value, nullOk); + if (passed) { + return val; + } else { + throw new Error("Typecheck failed! Expected: " + typeName.value); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + return root; + } + }); + + parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) { + if (!tokens.matchToken("not")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "logicalNot", + root: root, + args: [root], + op: function (context, val) { + return !val; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("noExpression", function (parser, runtime, tokens) { + if (!tokens.matchToken("no")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "noExpression", + root: root, + args: [root], + op: function (_context, val) { + return runtime.isEmpty(val); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addLeafExpression("some", function (parser, runtime, tokens) { + if (!tokens.matchToken("some")) return; + var root = parser.requireElement("expression", tokens); + return { + type: "noExpression", + root: root, + args: [root], + op: function (_context, val) { + return !runtime.isEmpty(val); + }, + evaluate(context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("-")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "negativeNumber", + root: root, + args: [root], + op: function (context, value) { + return -1 * value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) { + tokens.matchToken("the"); // optional "the" + return parser.parseAnyOf( + ["beepExpression", "logicalNot", "relativePositionalExpression", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"], + tokens + ); + }); + + parser.addGrammarElement("beepExpression", function (parser, runtime, tokens) { + if (!tokens.matchToken("beep!")) return; + var expression = parser.parseElement("unaryExpression", tokens); + if (expression) { + expression['booped'] = true; + var originalEvaluate = expression.evaluate; + expression.evaluate = function(ctx){ + let value = originalEvaluate.apply(expression, arguments); + let element = ctx.me; + runtime.beepValueToConsole(element, expression, value); + return value; + } + return expression; + } + }); + + var scanForwardQuery = function(start, root, match, wrap) { + var results = root.querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + if (wrap) { + return results[0]; + } + } + + var scanBackwardsQuery = function(start, root, match, wrap) { + var results = root.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; + } + } + if (wrap) { + return results[results.length - 1]; + } + } + + var scanForwardArray = function(start, array, match, wrap) { + var matches = []; + Runtime.prototype.forEach(array, function(elt){ + if (elt.matches(match) || elt === start) { + matches.push(elt); + } + }) + for (var i = 0; i < matches.length - 1; i++) { + var elt = matches[i]; + if (elt === start) { + return matches[i + 1]; + } + } + if (wrap) { + var first = matches[0]; + if (first && first.matches(match)) { + return first; + } + } + } + + var scanBackwardsArray = function(start, array, match, wrap) { + return scanForwardArray(start, Array.from(array).reverse(), match, wrap); + } + + parser.addGrammarElement("relativePositionalExpression", function (parser, runtime, tokens) { + var op = tokens.matchAnyToken("next", "previous"); + if (!op) return; + var forwardSearch = op.value === "next"; + + var thingElt = parser.parseElement("expression", tokens); + + if (tokens.matchToken("from")) { + tokens.pushFollow("in"); + try { + var from = parser.requireElement("unaryExpression", tokens); + } finally { + tokens.popFollow(); + } + } else { + var from = parser.requireElement("implicitMeTarget", tokens); + } + + var inSearch = false; + var withinElt; + if (tokens.matchToken("in")) { + inSearch = true; + var inElt = parser.requireElement("unaryExpression", tokens); + } else if (tokens.matchToken("within")) { + withinElt = parser.requireElement("unaryExpression", tokens); + } else { + withinElt = document.body; + } + + var wrapping = false; + if (tokens.matchToken("with")) { + tokens.requireToken("wrapping") + wrapping = true; + } + + return { + type: "relativePositionalExpression", + from: from, + forwardSearch: forwardSearch, + inSearch: inSearch, + wrapping: wrapping, + inElt: inElt, + withinElt: withinElt, + operator: op.value, + args: [thingElt, from, inElt, withinElt], + op: function (context, thing, from, inElt, withinElt) { + + var css = thing.css; + if (css == null) { + throw "Expected a CSS value to be returned by " + Tokens.sourceFor.apply(thingElt); + } + + if(inSearch) { + if (inElt) { + if (forwardSearch) { + return scanForwardArray(from, inElt, css, wrapping); + } else { + return scanBackwardsArray(from, inElt, css, wrapping); + } + } + } else { + if (withinElt) { + if (forwardSearch) { + return scanForwardQuery(from, withinElt, css, wrapping); + } else { + return scanBackwardsQuery(from, withinElt, css, wrapping); + } + } + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + } + + }); + + parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) { + var op = tokens.matchAnyToken("first", "last", "random"); + if (!op) return; + tokens.matchAnyToken("in", "from", "of"); + var rhs = parser.requireElement("unaryExpression", tokens); + const operator = op.value; + return { + type: "positionalExpression", + rhs: rhs, + operator: op.value, + args: [rhs], + op: function (context, rhsVal) { + if (rhsVal && !Array.isArray(rhsVal)) { + if (rhsVal.children) { + rhsVal = rhsVal.children; + } else { + rhsVal = Array.from(rhsVal); + } + } + if (rhsVal) { + if (operator === "first") { + return rhsVal[0]; + } else if (operator === "last") { + return rhsVal[rhsVal.length - 1]; + } else if (operator === "random") { + return rhsVal[Math.floor(Math.random() * rhsVal.length)]; + } + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("unaryExpression", tokens); + var mathOp, + initialMathOp = null; + mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod'); + while (mathOp) { + initialMathOp = initialMathOp || mathOp; + var operator = mathOp.value; + if (initialMathOp.value !== operator) { + parser.raiseParseError(tokens, "You must parenthesize math operations with different operators"); + } + var rhs = parser.parseElement("unaryExpression", tokens); + expr = { + type: "mathOperator", + lhs: expr, + rhs: rhs, + operator: operator, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (operator === "+") { + return lhsVal + rhsVal; + } else if (operator === "-") { + return lhsVal - rhsVal; + } else if (operator === "*") { + return lhsVal * rhsVal; + } else if (operator === "/") { + return lhsVal / rhsVal; + } else if (operator === "mod") { + return lhsVal % rhsVal; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod'); + } + return expr; + }); + + parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens); + }); + + function sloppyContains(src, container, value){ + if (container['contains']) { + return container.contains(value); + } else if (container['includes']) { + return container.includes(value); + } else { + throw Error("The value of " + src.sourceFor() + " does not have a contains or includes method on it"); + } + } + function sloppyMatches(src, target, toMatch){ + if (target['match']) { + return !!target.match(toMatch); + } else if (target['matches']) { + return target.matches(toMatch); + } else { + throw Error("The value of " + src.sourceFor() + " does not have a match or matches method on it"); + } + } + + parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("mathExpression", tokens); + var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!=="); + var operator = comparisonToken ? comparisonToken.value : null; + var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions. + var typeCheck = false; + + if (operator == null) { + if (tokens.matchToken("is") || tokens.matchToken("am")) { + if (tokens.matchToken("not")) { + if (tokens.matchToken("in")) { + operator = "not in"; + } else if (tokens.matchToken("a")) { + operator = "not a"; + typeCheck = true; + } else if (tokens.matchToken("empty")) { + operator = "not empty"; + hasRightValue = false; + } else { + if (tokens.matchToken("really")) { + operator = "!=="; + } else { + operator = "!="; + } + // consume additional optional syntax + if (tokens.matchToken("equal")) { + tokens.matchToken("to"); + } + } + } else if (tokens.matchToken("in")) { + operator = "in"; + } else if (tokens.matchToken("a")) { + operator = "a"; + typeCheck = true; + } else if (tokens.matchToken("empty")) { + operator = "empty"; + hasRightValue = false; + } else if (tokens.matchToken("less")) { + tokens.requireToken("than"); + if (tokens.matchToken("or")) { + tokens.requireToken("equal"); + tokens.requireToken("to"); + operator = "<="; + } else { + operator = "<"; + } + } else if (tokens.matchToken("greater")) { + tokens.requireToken("than"); + if (tokens.matchToken("or")) { + tokens.requireToken("equal"); + tokens.requireToken("to"); + operator = ">="; + } else { + operator = ">"; + } + } else { + if (tokens.matchToken("really")) { + operator = "==="; + } else { + operator = "=="; + } + if (tokens.matchToken("equal")) { + tokens.matchToken("to"); + } + } + } else if (tokens.matchToken("equals")) { + operator = "=="; + } else if (tokens.matchToken("really")) { + tokens.requireToken("equals") + operator = "==="; + } else if (tokens.matchToken("exist") || tokens.matchToken("exists")) { + operator = "exist"; + hasRightValue = false; + } else if (tokens.matchToken("matches") || tokens.matchToken("match")) { + operator = "match"; + } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { + operator = "contain"; + } else if (tokens.matchToken("includes") || tokens.matchToken("include")) { + operator = "include"; + } else if (tokens.matchToken("do") || tokens.matchToken("does")) { + tokens.requireToken("not"); + if (tokens.matchToken("matches") || tokens.matchToken("match")) { + operator = "not match"; + } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { + operator = "not contain"; + } else if (tokens.matchToken("exist") || tokens.matchToken("exist")) { + operator = "not exist"; + hasRightValue = false; + } else if (tokens.matchToken("include")) { + operator = "not include"; + } else { + parser.raiseParseError(tokens, "Expected matches or contains"); + } + } + } + + if (operator) { + // Do not allow chained comparisons, which is dumb + var typeName, nullOk, rhs + if (typeCheck) { + typeName = tokens.requireTokenType("IDENTIFIER"); + nullOk = !tokens.matchOpToken("!"); + } else if (hasRightValue) { + rhs = parser.requireElement("mathExpression", tokens); + if (operator === "match" || operator === "not match") { + rhs = rhs.css ? rhs.css : rhs; + } + } + var lhs = expr; + expr = { + type: "comparisonOperator", + operator: operator, + typeName: typeName, + nullOk: nullOk, + lhs: expr, + rhs: rhs, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (operator === "==") { + return lhsVal == rhsVal; + } else if (operator === "!=") { + return lhsVal != rhsVal; + } + if (operator === "===") { + return lhsVal === rhsVal; + } else if (operator === "!==") { + return lhsVal !== rhsVal; + } + if (operator === "match") { + return lhsVal != null && sloppyMatches(lhs, lhsVal, rhsVal); + } + if (operator === "not match") { + return lhsVal == null || !sloppyMatches(lhs, lhsVal, rhsVal); + } + if (operator === "in") { + return rhsVal != null && sloppyContains(rhs, rhsVal, lhsVal); + } + if (operator === "not in") { + return rhsVal == null || !sloppyContains(rhs, rhsVal, lhsVal); + } + if (operator === "contain") { + return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal); + } + if (operator === "not contain") { + return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal); + } + if (operator === "include") { + return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal); + } + if (operator === "not include") { + return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal); + } + if (operator === "===") { + return lhsVal === rhsVal; + } else if (operator === "!==") { + return lhsVal !== rhsVal; + } else if (operator === "<") { + return lhsVal < rhsVal; + } else if (operator === ">") { + return lhsVal > rhsVal; + } else if (operator === "<=") { + return lhsVal <= rhsVal; + } else if (operator === ">=") { + return lhsVal >= rhsVal; + } else if (operator === "empty") { + return runtime.isEmpty(lhsVal); + } else if (operator === "not empty") { + return !runtime.isEmpty(lhsVal); + } else if (operator === "exist") { + return runtime.doesExist(lhsVal); + } else if (operator === "not exist") { + return !runtime.doesExist(lhsVal); + } else if (operator === "a") { + return runtime.typeCheck(lhsVal, typeName.value, nullOk); + } else if (operator === "not a") { + return !runtime.typeCheck(lhsVal, typeName.value, nullOk); + } else { + throw "Unknown comparison : " + operator; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + return expr; + }); + + parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens); + }); + + parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("comparisonExpression", tokens); + var logicalOp, + initialLogicalOp = null; + logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); + while (logicalOp) { + initialLogicalOp = initialLogicalOp || logicalOp; + if (initialLogicalOp.value !== logicalOp.value) { + parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators"); + } + var rhs = parser.requireElement("comparisonExpression", tokens); + const operator = logicalOp.value; + expr = { + type: "logicalOperator", + operator: operator, + lhs: expr, + rhs: rhs, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (operator === "and") { + return lhsVal && rhsVal; + } else { + return lhsVal || rhsVal; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); + } + return expr; + }); + + parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens); + }); + + parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) { + if (tokens.matchToken("async")) { + var value = parser.requireElement("logicalExpression", tokens); + var expr = { + type: "asyncExpression", + value: value, + evaluate: function (context) { + return { + asyncWrapper: true, + value: this.value.evaluate(context), //OK + }; + }, + }; + return expr; + } else { + return parser.parseElement("logicalExpression", tokens); + } + }); + + parser.addGrammarElement("expression", function (parser, runtime, tokens) { + tokens.matchToken("the"); // optional the + return parser.parseElement("asyncExpression", tokens); + }); + + parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) { + tokens.matchToken("the"); // optional the + + // TODO obviously we need to generalize this as a left hand side / targetable concept + var expr = parser.parseElement("primaryExpression", tokens); + if (expr && ( + expr.type === "symbol" || + expr.type === "ofExpression" || + expr.type === "propertyAccess" || + expr.type === "attributeRefAccess" || + expr.type === "attributeRef" || + expr.type === "styleRef" || + expr.type === "arrayIndex" || + expr.type === "possessive") + ) { + return expr; + } else { + parser.raiseParseError( + tokens, + "A target expression must be writable. The expression type '" + (expr && expr.type) + "' is not." + ); + } + return expr; + }); + + parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) { + var features = []; + + if (tokens.hasMore()) { + while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") { + var feature = parser.requireElement("feature", tokens); + features.push(feature); + tokens.matchToken("end"); // optional end + } + } + return { + type: "hyperscript", + features: features, + apply: function (target, source, args) { + // no op + for (const feature of features) { + feature.install(target, source, args); + } + }, + }; + }); + + var parseEventArgs = function (tokens) { + var args = []; + // handle argument list (look ahead 3) + if ( + tokens.token(0).value === "(" && + (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")") + ) { + tokens.matchOpToken("("); + do { + args.push(tokens.requireTokenType("IDENTIFIER")); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + return args; + }; + + parser.addFeature("on", function (parser, runtime, tokens) { + if (!tokens.matchToken("on")) return; + var every = false; + if (tokens.matchToken("every")) { + every = true; + } + var events = []; + var displayName = null; + do { + var on = parser.requireElement("eventName", tokens, "Expected event name"); + + var eventName = on.evaluate(); // OK No Promise + + if (displayName) { + displayName = displayName + " or " + eventName; + } else { + displayName = "on " + eventName; + } + var args = parseEventArgs(tokens); + + var filter = null; + if (tokens.matchOpToken("[")) { + filter = parser.requireElement("expression", tokens); + tokens.requireOpToken("]"); + } + + var startCount, endCount ,unbounded; + if (tokens.currentToken().type === "NUMBER") { + var startCountToken = tokens.consumeToken(); + if (!startCountToken.value) return; + startCount = parseInt(startCountToken.value); + if (tokens.matchToken("to")) { + var endCountToken = tokens.consumeToken(); + if (!endCountToken.value) return; + endCount = parseInt(endCountToken.value); + } else if (tokens.matchToken("and")) { + unbounded = true; + tokens.requireToken("on"); + } + } + + var intersectionSpec, mutationSpec; + if (eventName === "intersection") { + intersectionSpec = {}; + if (tokens.matchToken("with")) { + intersectionSpec["with"] = parser.requireElement("expression", tokens).evaluate(); + } + if (tokens.matchToken("having")) { + do { + if (tokens.matchToken("margin")) { + intersectionSpec["rootMargin"] = parser.requireElement("stringLike", tokens).evaluate(); + } else if (tokens.matchToken("threshold")) { + intersectionSpec["threshold"] = parser.requireElement("expression", tokens).evaluate(); + } else { + parser.raiseParseError(tokens, "Unknown intersection config specification"); + } + } while (tokens.matchToken("and")); + } + } else if (eventName === "mutation") { + mutationSpec = {}; + if (tokens.matchToken("of")) { + do { + if (tokens.matchToken("anything")) { + mutationSpec["attributes"] = true; + mutationSpec["subtree"] = true; + mutationSpec["characterData"] = true; + mutationSpec["childList"] = true; + } else if (tokens.matchToken("childList")) { + mutationSpec["childList"] = true; + } else if (tokens.matchToken("attributes")) { + mutationSpec["attributes"] = true; + mutationSpec["attributeOldValue"] = true; + } else if (tokens.matchToken("subtree")) { + mutationSpec["subtree"] = true; + } else if (tokens.matchToken("characterData")) { + mutationSpec["characterData"] = true; + mutationSpec["characterDataOldValue"] = true; + } else if (tokens.currentToken().type === "ATTRIBUTE_REF") { + var attribute = tokens.consumeToken(); + if (mutationSpec["attributeFilter"] == null) { + mutationSpec["attributeFilter"] = []; + } + if (attribute.value.indexOf("@") == 0) { + mutationSpec["attributeFilter"].push(attribute.value.substring(1)); + } else { + parser.raiseParseError( + tokens, + "Only shorthand attribute references are allowed here" + ); + } + } else { + parser.raiseParseError(tokens, "Unknown mutation config specification"); + } + } while (tokens.matchToken("or")); + } else { + mutationSpec["attributes"] = true; + mutationSpec["characterData"] = true; + mutationSpec["childList"] = true; + } + } + + var from = null; + var elsewhere = false; + if (tokens.matchToken("from")) { + if (tokens.matchToken("elsewhere")) { + elsewhere = true; + } else { + tokens.pushFollow("or"); + try { + from = parser.requireElement("expression", tokens) + } finally { + tokens.popFollow(); + } + if (!from) { + parser.raiseParseError(tokens, 'Expected either target value or "elsewhere".'); + } + } + } + // support both "elsewhere" and "from elsewhere" + if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) { + elsewhere = true; + } + + if (tokens.matchToken("in")) { + var inExpr = parser.parseElement('unaryExpression', tokens); + } + + if (tokens.matchToken("debounced")) { + tokens.requireToken("at"); + var timeExpr = parser.requireElement("unaryExpression", tokens); + // @ts-ignore + var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr + } else if (tokens.matchToken("throttled")) { + tokens.requireToken("at"); + var timeExpr = parser.requireElement("unaryExpression", tokens); + // @ts-ignore + var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr + } + + events.push({ + execCount: 0, + every: every, + on: eventName, + args: args, + filter: filter, + from: from, + inExpr: inExpr, + elsewhere: elsewhere, + startCount: startCount, + endCount: endCount, + unbounded: unbounded, + debounceTime: debounceTime, + throttleTime: throttleTime, + mutationSpec: mutationSpec, + intersectionSpec: intersectionSpec, + debounced: undefined, + lastExec: undefined, + }); + } while (tokens.matchToken("or")); + + var queueLast = true; + if (!every) { + if (tokens.matchToken("queue")) { + if (tokens.matchToken("all")) { + var queueAll = true; + var queueLast = false; + } else if (tokens.matchToken("first")) { + var queueFirst = true; + } else if (tokens.matchToken("none")) { + var queueNone = true; + } else { + tokens.requireToken("last"); + } + } + } + + var start = parser.requireElement("commandList", tokens); + parser.ensureTerminated(start); + + var errorSymbol, errorHandler; + if (tokens.matchToken("catch")) { + errorSymbol = tokens.requireTokenType("IDENTIFIER").value; + errorHandler = parser.requireElement("commandList", tokens); + parser.ensureTerminated(errorHandler); + } + + if (tokens.matchToken("finally")) { + var finallyHandler = parser.requireElement("commandList", tokens); + parser.ensureTerminated(finallyHandler); + } + + var onFeature = { + displayName: displayName, + events: events, + start: start, + every: every, + execCount: 0, + errorHandler: errorHandler, + errorSymbol: errorSymbol, + execute: function (/** @type {Context} */ ctx) { + let eventQueueInfo = runtime.getEventQueueFor(ctx.me, onFeature); + if (eventQueueInfo.executing && every === false) { + if (queueNone || (queueFirst && eventQueueInfo.queue.length > 0)) { + return; + } + if (queueLast) { + eventQueueInfo.queue.length = 0; + } + eventQueueInfo.queue.push(ctx); + return; + } + onFeature.execCount++; + eventQueueInfo.executing = true; + ctx.meta.onHalt = function () { + eventQueueInfo.executing = false; + var queued = eventQueueInfo.queue.shift(); + if (queued) { + setTimeout(function () { + onFeature.execute(queued); + }, 1); + } + }; + ctx.meta.reject = function (err) { + console.error(err.message ? err.message : err); + var hypertrace = runtime.getHyperTrace(ctx, err); + if (hypertrace) { + hypertrace.print(); + } + runtime.triggerEvent(ctx.me, "exception", { + error: err, + }); + }; + start.execute(ctx); + }, + install: function (elt, source) { + for (const eventSpec of onFeature.events) { + var targets; + if (eventSpec.elsewhere) { + targets = [document]; + } else if (eventSpec.from) { + targets = eventSpec.from.evaluate(runtime.makeContext(elt, onFeature, elt, null)); + } else { + targets = [elt]; + } + runtime.implicitLoop(targets, function (target) { + // OK NO PROMISE + + var eventName = eventSpec.on; + if (target == null) { + console.warn("'%s' feature ignored because target does not exists:", displayName, elt); + return; + } + + if (eventSpec.mutationSpec) { + eventName = "hyperscript:mutation"; + const observer = new MutationObserver(function (mutationList, observer) { + if (!onFeature.executing) { + runtime.triggerEvent(target, eventName, { + mutationList: mutationList, + observer: observer, + }); + } + }); + observer.observe(target, eventSpec.mutationSpec); + } + + if (eventSpec.intersectionSpec) { + eventName = "hyperscript:intersection"; + const observer = new IntersectionObserver(function (entries) { + for (const entry of entries) { + var detail = { + observer: observer, + }; + detail = Object.assign(detail, entry); + detail["intersecting"] = entry.isIntersecting; + runtime.triggerEvent(target, eventName, detail); + } + }, eventSpec.intersectionSpec); + observer.observe(target); + } + + var addEventListener = target.addEventListener || target.on; + addEventListener.call(target, eventName, function listener(evt) { + // OK NO PROMISE + if (typeof Node !== 'undefined' && elt instanceof Node && target !== elt && !elt.isConnected) { + target.removeEventListener(eventName, listener); + return; + } + + var ctx = runtime.makeContext(elt, onFeature, elt, evt); + if (eventSpec.elsewhere && elt.contains(evt.target)) { + return; + } + if (eventSpec.from) { + ctx.result = target; + } + + // establish context + for (const arg of eventSpec.args) { + let eventValue = ctx.event[arg.value]; + if (eventValue !== undefined) { + ctx.locals[arg.value] = eventValue; + } else if ('detail' in ctx.event) { + ctx.locals[arg.value] = ctx.event['detail'][arg.value]; + } + } + + // install error handler if any + ctx.meta.errorHandler = errorHandler; + ctx.meta.errorSymbol = errorSymbol; + ctx.meta.finallyHandler = finallyHandler; + + // apply filter + if (eventSpec.filter) { + var initialCtx = ctx.meta.context; + ctx.meta.context = ctx.event; + try { + var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE + if (value) { + // match the javascript semantics for if statements + } else { + return; + } + } finally { + ctx.meta.context = initialCtx; + } + } + + if (eventSpec.inExpr) { + var inElement = evt.target; + while (true) { + if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) { + ctx.result = inElement; + break; + } else { + inElement = inElement.parentElement; + if (inElement == null) { + return; // no match found + } + } + } + } + + // verify counts + eventSpec.execCount++; + if (eventSpec.startCount) { + if (eventSpec.endCount) { + if ( + eventSpec.execCount < eventSpec.startCount || + eventSpec.execCount > eventSpec.endCount + ) { + return; + } + } else if (eventSpec.unbounded) { + if (eventSpec.execCount < eventSpec.startCount) { + return; + } + } else if (eventSpec.execCount !== eventSpec.startCount) { + return; + } + } + + //debounce + if (eventSpec.debounceTime) { + if (eventSpec.debounced) { + clearTimeout(eventSpec.debounced); + } + eventSpec.debounced = setTimeout(function () { + onFeature.execute(ctx); + }, eventSpec.debounceTime); + return; + } + + // throttle + if (eventSpec.throttleTime) { + if ( + eventSpec.lastExec && + Date.now() < (eventSpec.lastExec + eventSpec.throttleTime) + ) { + return; + } else { + eventSpec.lastExec = Date.now(); + } + } + + // apply execute + onFeature.execute(ctx); + }); + }); + } + }, + }; + parser.setParent(start, onFeature); + return onFeature; + }); + + parser.addFeature("def", function (parser, runtime, tokens) { + if (!tokens.matchToken("def")) return; + var functionName = parser.requireElement("dotOrColonPath", tokens); + var nameVal = functionName.evaluate(); // OK + var nameSpace = nameVal.split("."); + var funcName = nameSpace.pop(); + + var args = []; + if (tokens.matchOpToken("(")) { + if (tokens.matchOpToken(")")) { + // emtpy args list + } else { + do { + args.push(tokens.requireTokenType("IDENTIFIER")); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + } + + var start = parser.requireElement("commandList", tokens); + + var errorSymbol, errorHandler; + if (tokens.matchToken("catch")) { + errorSymbol = tokens.requireTokenType("IDENTIFIER").value; + errorHandler = parser.parseElement("commandList", tokens); + } + + if (tokens.matchToken("finally")) { + var finallyHandler = parser.requireElement("commandList", tokens); + parser.ensureTerminated(finallyHandler); + } + + var functionFeature = { + displayName: + funcName + + "(" + + args + .map(function (arg) { + return arg.value; + }) + .join(", ") + + ")", + name: funcName, + args: args, + start: start, + errorHandler: errorHandler, + errorSymbol: errorSymbol, + finallyHandler: finallyHandler, + install: function (target, source) { + var func = function () { + // null, worker + var ctx = runtime.makeContext(source, functionFeature, target, null); + + // install error handler if any + ctx.meta.errorHandler = errorHandler; + ctx.meta.errorSymbol = errorSymbol; + ctx.meta.finallyHandler = finallyHandler; + + for (var i = 0; i < args.length; i++) { + var name = args[i]; + var argumentVal = arguments[i]; + if (name) { + ctx.locals[name.value] = argumentVal; + } + } + ctx.meta.caller = arguments[args.length]; + if (ctx.meta.caller) { + ctx.meta.callingCommand = ctx.meta.caller.meta.command; + } + var resolve, + reject = null; + var promise = new Promise(function (theResolve, theReject) { + resolve = theResolve; + reject = theReject; + }); + start.execute(ctx); + if (ctx.meta.returned) { + return ctx.meta.returnValue; + } else { + ctx.meta.resolve = resolve; + ctx.meta.reject = reject; + return promise; + } + }; + func.hyperfunc = true; + func.hypername = nameVal; + runtime.assignToNamespace(target, nameSpace, funcName, func); + }, + }; + + parser.ensureTerminated(start); + + // terminate error handler if any + if (errorHandler) { + parser.ensureTerminated(errorHandler); + } + + parser.setParent(start, functionFeature); + return functionFeature; + }); + + parser.addFeature("set", function (parser, runtime, tokens) { + let setCmd = parser.parseElement("setCommand", tokens); + if (setCmd) { + if (setCmd.target.scope !== "element") { + parser.raiseParseError(tokens, "variables declared at the feature level must be element scoped."); + } + let setFeature = { + start: setCmd, + install: function (target, source) { + setCmd && setCmd.execute(runtime.makeContext(target, setFeature, target, null)); + }, + }; + parser.ensureTerminated(setCmd); + return setFeature; + } + }); + + parser.addFeature("init", function (parser, runtime, tokens) { + if (!tokens.matchToken("init")) return; + + var immediately = tokens.matchToken("immediately"); + + var start = parser.requireElement("commandList", tokens); + var initFeature = { + start: start, + install: function (target, source) { + let handler = function () { + start && start.execute(runtime.makeContext(target, initFeature, target, null)); + }; + if (immediately) { + handler(); + } else { + setTimeout(handler, 0); + } + }, + }; + + // terminate body + parser.ensureTerminated(start); + parser.setParent(start, initFeature); + return initFeature; + }); + + parser.addFeature("worker", function (parser, runtime, tokens) { + if (tokens.matchToken("worker")) { + parser.raiseParseError( + tokens, + "In order to use the 'worker' feature, include " + + "the _hyperscript worker plugin. See " + + "https://hyperscript.org/features/worker/ for " + + "more info." + ); + return undefined + } + }); + + parser.addFeature("behavior", function (parser, runtime, tokens) { + if (!tokens.matchToken("behavior")) return; + var path = parser.requireElement("dotOrColonPath", tokens).evaluate(); + var nameSpace = path.split("."); + var name = nameSpace.pop(); + + var formalParams = []; + if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) { + do { + formalParams.push(tokens.requireTokenType("IDENTIFIER").value); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + var hs = parser.requireElement("hyperscript", tokens); + for (var i = 0; i < hs.features.length; i++) { + var feature = hs.features[i]; + feature.behavior = path; + } + + return { + install: function (target, source) { + runtime.assignToNamespace( + globalScope.document && globalScope.document.body, + nameSpace, + name, + function (target, source, innerArgs) { + var internalData = runtime.getInternalData(target); + var elementScope = getOrInitObject(internalData, path + "Scope"); + for (var i = 0; i < formalParams.length; i++) { + elementScope[formalParams[i]] = innerArgs[formalParams[i]]; + } + hs.apply(target, source); + } + ); + }, + }; + }); + + parser.addFeature("install", function (parser, runtime, tokens) { + if (!tokens.matchToken("install")) return; + var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate(); + var behaviorNamespace = behaviorPath.split("."); + var args = parser.parseElement("namedArgumentList", tokens); + + var installFeature; + return (installFeature = { + install: function (target, source) { + runtime.unifiedEval( + { + args: [args], + op: function (ctx, args) { + var behavior = globalScope; + for (var i = 0; i < behaviorNamespace.length; i++) { + behavior = behavior[behaviorNamespace[i]]; + if (typeof behavior !== "object" && typeof behavior !== "function") + throw new Error("No such behavior defined as " + behaviorPath); + } + + if (!(behavior instanceof Function)) + throw new Error(behaviorPath + " is not a behavior"); + + behavior(target, source, args); + }, + }, + runtime.makeContext(target, installFeature, target, null) + ); + }, + }); + }); + + parser.addGrammarElement("jsBody", function (parser, runtime, tokens) { + var jsSourceStart = tokens.currentToken().start; + var jsLastToken = tokens.currentToken(); + + var funcNames = []; + var funcName = ""; + var expectFunctionDeclaration = false; + while (tokens.hasMore()) { + jsLastToken = tokens.consumeToken(); + var peek = tokens.token(0, true); + if (peek.type === "IDENTIFIER" && peek.value === "end") { + break; + } + if (expectFunctionDeclaration) { + if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") { + funcName += jsLastToken.value; + } else { + if (funcName !== "") funcNames.push(funcName); + funcName = ""; + expectFunctionDeclaration = false; + } + } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") { + expectFunctionDeclaration = true; + } + } + var jsSourceEnd = jsLastToken.end + 1; + + return { + type: "jsBody", + exposedFunctionNames: funcNames, + jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd), + }; + }); + + parser.addFeature("js", function (parser, runtime, tokens) { + if (!tokens.matchToken("js")) return; + var jsBody = parser.requireElement("jsBody", tokens); + + var jsSource = + jsBody.jsSource + + "\nreturn { " + + jsBody.exposedFunctionNames + .map(function (name) { + return name + ":" + name; + }) + .join(",") + + " } "; + var func = new Function(jsSource); + + return { + jsSource: jsSource, + function: func, + exposedFunctionNames: jsBody.exposedFunctionNames, + install: function () { + Object.assign(globalScope, func()); + }, + }; + }); + + parser.addCommand("js", function (parser, runtime, tokens) { + if (!tokens.matchToken("js")) return; + // Parse inputs + var inputs = []; + if (tokens.matchOpToken("(")) { + if (tokens.matchOpToken(")")) { + // empty input list + } else { + do { + var inp = tokens.requireTokenType("IDENTIFIER"); + inputs.push(inp.value); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + } + + var jsBody = parser.requireElement("jsBody", tokens); + tokens.matchToken("end"); + + var func = varargConstructor(Function, inputs.concat([jsBody.jsSource])); + + var command = { + jsSource: jsBody.jsSource, + function: func, + inputs: inputs, + op: function (context) { + var args = []; + inputs.forEach(function (input) { + args.push(runtime.resolveSymbol(input, context, 'default')); + }); + var result = func.apply(globalScope, args); + if (result && typeof result.then === "function") { + return new Promise(function (resolve) { + result.then(function (actualResult) { + context.result = actualResult; + resolve(runtime.findNext(this, context)); + }); + }); + } else { + context.result = result; + return runtime.findNext(this, context); + } + }, + }; + return command; + }); + + parser.addCommand("async", function (parser, runtime, tokens) { + if (!tokens.matchToken("async")) return; + if (tokens.matchToken("do")) { + var body = parser.requireElement("commandList", tokens); + + // Append halt + var end = body; + while (end.next) end = end.next; + end.next = runtime.HALT; + + tokens.requireToken("end"); + } else { + var body = parser.requireElement("command", tokens); + } + var command = { + body: body, + op: function (context) { + setTimeout(function () { + body.execute(context); + }); + return runtime.findNext(this, context); + }, + }; + parser.setParent(body, command); + return command; + }); + + parser.addCommand("tell", function (parser, runtime, tokens) { + var startToken = tokens.currentToken(); + if (!tokens.matchToken("tell")) return; + var value = parser.requireElement("expression", tokens); + var body = parser.requireElement("commandList", tokens); + if (tokens.hasMore() && !parser.featureStart(tokens.currentToken())) { + tokens.requireToken("end"); + } + var slot = "tell_" + startToken.start; + var tellCmd = { + value: value, + body: body, + args: [value], + resolveNext: function (context) { + var iterator = context.meta.iterators[slot]; + if (iterator.index < iterator.value.length) { + context.you = iterator.value[iterator.index++]; + return body; + } else { + // restore original me + context.you = iterator.originalYou; + if (this.next) { + return this.next; + } else { + return runtime.findNext(this.parent, context); + } + } + }, + op: function (context, value) { + if (value == null) { + value = []; + } else if (!(Array.isArray(value) || value instanceof NodeList)) { + value = [value]; + } + context.meta.iterators[slot] = { + originalYou: context.you, + index: 0, + value: value, + }; + return this.resolveNext(context); + }, + }; + parser.setParent(body, tellCmd); + return tellCmd; + }); + + parser.addCommand("wait", function (parser, runtime, tokens) { + if (!tokens.matchToken("wait")) return; + var command; + + // wait on event + if (tokens.matchToken("for")) { + tokens.matchToken("a"); // optional "a" + var events = []; + do { + var lookahead = tokens.token(0); + if (lookahead.type === 'NUMBER' || lookahead.type === 'L_PAREN') { + events.push({ + time: parser.requireElement('expression', tokens).evaluate() // TODO: do we want to allow async here? + }) + } else { + events.push({ + name: parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(), + args: parseEventArgs(tokens), + }); + } + } while (tokens.matchToken("or")); + + if (tokens.matchToken("from")) { + var on = parser.requireElement("expression", tokens); + } + + // wait on event + command = { + event: events, + on: on, + args: [on], + op: function (context, on) { + var target = on ? on : context.me; + if (!(target instanceof EventTarget)) + throw new Error("Not a valid event target: " + this.on.sourceFor()); + return new Promise((resolve) => { + var resolved = false; + for (const eventInfo of events) { + var listener = (event) => { + context.result = event; + if (eventInfo.args) { + for (const arg of eventInfo.args) { + context.locals[arg.value] = + event[arg.value] || (event.detail ? event.detail[arg.value] : null); + } + } + if (!resolved) { + resolved = true; + resolve(runtime.findNext(this, context)); + } + }; + if (eventInfo.name){ + target.addEventListener(eventInfo.name, listener, {once: true}); + } else if (eventInfo.time != null) { + setTimeout(listener, eventInfo.time, eventInfo.time) + } + } + }); + }, + }; + return command; + } else { + var time; + if (tokens.matchToken("a")) { + tokens.requireToken("tick"); + time = 0; + } else { + time = parser.requireElement("expression", tokens); + } + + command = { + type: "waitCmd", + time: time, + args: [time], + op: function (context, timeValue) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(runtime.findNext(this, context)); + }, timeValue); + }); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + return command; + } + }); + + // TODO - colon path needs to eventually become part of ruby-style symbols + parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) { + var root = tokens.matchTokenType("IDENTIFIER"); + if (root) { + var path = [root.value]; + + var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":"); + if (separator) { + do { + path.push(tokens.requireTokenType("IDENTIFIER", "NUMBER").value); + } while (tokens.matchOpToken(separator.value)); + } + + return { + type: "dotOrColonPath", + path: path, + evaluate: function () { + return path.join(separator ? separator.value : ""); + }, + }; + } + }); + + + parser.addGrammarElement("eventName", function (parser, runtime, tokens) { + var token; + if ((token = tokens.matchTokenType("STRING"))) { + return { + evaluate: function() { + return token.value; + }, + }; + } + + return parser.parseElement("dotOrColonPath", tokens); + }); + + function parseSendCmd(cmdType, parser, runtime, tokens) { + var eventName = parser.requireElement("eventName", tokens); + + var details = parser.parseElement("namedArgumentList", tokens); + if ((cmdType === "send" && tokens.matchToken("to")) || + (cmdType === "trigger" && tokens.matchToken("on"))) { + var toExpr = parser.requireElement("expression", tokens); + } else { + var toExpr = parser.requireElement("implicitMeTarget", tokens); + } + + var sendCmd = { + eventName: eventName, + details: details, + to: toExpr, + args: [toExpr, eventName, details], + op: function (context, to, eventName, details) { + runtime.nullCheck(to, toExpr); + runtime.implicitLoop(to, function (target) { + runtime.triggerEvent(target, eventName, details, context.me); + }); + return runtime.findNext(sendCmd, context); + }, + }; + return sendCmd; + } + + parser.addCommand("trigger", function (parser, runtime, tokens) { + if (tokens.matchToken("trigger")) { + return parseSendCmd("trigger", parser, runtime, tokens); + } + }); + + parser.addCommand("send", function (parser, runtime, tokens) { + if (tokens.matchToken("send")) { + return parseSendCmd("send", parser, runtime, tokens); + } + }); + + var parseReturnFunction = function (parser, runtime, tokens, returnAValue) { + if (returnAValue) { + if (parser.commandBoundary(tokens.currentToken())) { + parser.raiseParseError(tokens, "'return' commands must return a value. If you do not wish to return a value, use 'exit' instead."); + } else { + var value = parser.requireElement("expression", tokens); + } + } + + var returnCmd = { + value: value, + args: [value], + op: function (context, value) { + var resolve = context.meta.resolve; + context.meta.returned = true; + context.meta.returnValue = value; + if (resolve) { + if (value) { + resolve(value); + } else { + resolve(); + } + } + return runtime.HALT; + }, + }; + return returnCmd; + }; + + parser.addCommand("return", function (parser, runtime, tokens) { + if (tokens.matchToken("return")) { + return parseReturnFunction(parser, runtime, tokens, true); + } + }); + + parser.addCommand("exit", function (parser, runtime, tokens) { + if (tokens.matchToken("exit")) { + return parseReturnFunction(parser, runtime, tokens, false); + } + }); + + parser.addCommand("halt", function (parser, runtime, tokens) { + if (tokens.matchToken("halt")) { + if (tokens.matchToken("the")) { + tokens.requireToken("event"); + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + var keepExecuting = true; + } + if (tokens.matchToken("bubbling")) { + var bubbling = true; + } else if (tokens.matchToken("default")) { + var haltDefault = true; + } + var exit = parseReturnFunction(parser, runtime, tokens, false); + + var haltCmd = { + keepExecuting: true, + bubbling: bubbling, + haltDefault: haltDefault, + exit: exit, + op: function (ctx) { + if (ctx.event) { + if (bubbling) { + ctx.event.stopPropagation(); + } else if (haltDefault) { + ctx.event.preventDefault(); + } else { + ctx.event.stopPropagation(); + ctx.event.preventDefault(); + } + if (keepExecuting) { + return runtime.findNext(this, ctx); + } else { + return exit; + } + } + }, + }; + return haltCmd; + } + }); + + parser.addCommand("log", function (parser, runtime, tokens) { + if (!tokens.matchToken("log")) return; + var exprs = [parser.parseElement("expression", tokens)]; + while (tokens.matchOpToken(",")) { + exprs.push(parser.requireElement("expression", tokens)); + } + if (tokens.matchToken("with")) { + var withExpr = parser.requireElement("expression", tokens); + } + var logCmd = { + exprs: exprs, + withExpr: withExpr, + args: [withExpr, exprs], + op: function (ctx, withExpr, values) { + if (withExpr) { + withExpr.apply(null, values); + } else { + console.log.apply(null, values); + } + return runtime.findNext(this, ctx); + }, + }; + return logCmd; + }); + + parser.addCommand("beep!", function (parser, runtime, tokens) { + if (!tokens.matchToken("beep!")) return; + var exprs = [parser.parseElement("expression", tokens)]; + while (tokens.matchOpToken(",")) { + exprs.push(parser.requireElement("expression", tokens)); + } + var beepCmd = { + exprs: exprs, + args: [exprs], + op: function (ctx, values) { + for (let i = 0; i < exprs.length; i++) { + const expr = exprs[i]; + const val = values[i]; + runtime.beepValueToConsole(ctx.me, expr, val); + } + return runtime.findNext(this, ctx); + }, + }; + return beepCmd; + }); + + parser.addCommand("throw", function (parser, runtime, tokens) { + if (!tokens.matchToken("throw")) return; + var expr = parser.requireElement("expression", tokens); + var throwCmd = { + expr: expr, + args: [expr], + op: function (ctx, expr) { + runtime.registerHyperTrace(ctx, expr); + throw expr; + }, + }; + return throwCmd; + }); + + var parseCallOrGet = function (parser, runtime, tokens) { + var expr = parser.requireElement("expression", tokens); + var callCmd = { + expr: expr, + args: [expr], + op: function (context, result) { + context.result = result; + return runtime.findNext(callCmd, context); + }, + }; + return callCmd; + }; + parser.addCommand("call", function (parser, runtime, tokens) { + if (!tokens.matchToken("call")) return; + var call = parseCallOrGet(parser, runtime, tokens); + if (call.expr && call.expr.type !== "functionCall") { + parser.raiseParseError(tokens, "Must be a function invocation"); + } + return call; + }); + parser.addCommand("get", function (parser, runtime, tokens) { + if (tokens.matchToken("get")) { + return parseCallOrGet(parser, runtime, tokens); + } + }); + + parser.addCommand("make", function (parser, runtime, tokens) { + if (!tokens.matchToken("make")) return; + tokens.matchToken("a") || tokens.matchToken("an"); + + var expr = parser.requireElement("expression", tokens); + + var args = []; + if (expr.type !== "queryRef" && tokens.matchToken("from")) { + do { + args.push(parser.requireElement("expression", tokens)); + } while (tokens.matchOpToken(",")); + } + + if (tokens.matchToken("called")) { + var target = parser.requireElement("symbol", tokens); + } + + var command; + if (expr.type === "queryRef") { + command = { + op: function (ctx) { + var match, + tagname = "div", + id, + classes = []; + var re = /(?:(^|#|\.)([^#\. ]+))/g; + while ((match = re.exec(expr.css))) { + if (match[1] === "") tagname = match[2].trim(); + else if (match[1] === "#") id = match[2].trim(); + else classes.push(match[2].trim()); + } + + var result = document.createElement(tagname); + if (id !== undefined) result.id = id; + for (var i = 0; i < classes.length; i++) { + var cls = classes[i]; + result.classList.add(cls) + } + + ctx.result = result; + if (target){ + runtime.setSymbol(target.name, ctx, target.scope, result); + } + + return runtime.findNext(this, ctx); + }, + }; + return command; + } else { + command = { + args: [expr, args], + op: function (ctx, expr, args) { + ctx.result = varargConstructor(expr, args); + if (target){ + runtime.setSymbol(target.name, ctx, target.scope, ctx.result); + } + + return runtime.findNext(this, ctx); + }, + }; + return command; + } + }); + + parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) { + + let lookAhead = tokens.token(1); + if (!(lookAhead && lookAhead.op && (lookAhead.value === '.' || lookAhead.value === "("))) { + return null; + } + + var expr = parser.requireElement("primaryExpression", tokens); + + var rootRoot = expr.root; + var root = expr; + while (rootRoot.root != null) { + root = root.root; + rootRoot = rootRoot.root; + } + + if (expr.type !== "functionCall") { + parser.raiseParseError(tokens, "Pseudo-commands must be function calls"); + } + + if (root.type === "functionCall" && root.root.root == null) { + if (tokens.matchAnyToken("the", "to", "on", "with", "into", "from", "at")) { + var realRoot = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("me")) { + var realRoot = parser.requireElement("implicitMeTarget", tokens); + } + } + + /** @type {ASTNode} */ + + var pseudoCommand + if(realRoot){ + pseudoCommand = { + type: "pseudoCommand", + root: realRoot, + argExressions: root.argExressions, + args: [realRoot, root.argExressions], + op: function (context, rootRoot, args) { + runtime.nullCheck(rootRoot, realRoot); + var func = rootRoot[root.root.name]; + runtime.nullCheck(func, root); + if (func.hyperfunc) { + args.push(context); + } + context.result = func.apply(rootRoot, args); + return runtime.findNext(pseudoCommand, context); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + } + } else { + pseudoCommand = { + type: "pseudoCommand", + expr: expr, + args: [expr], + op: function (context, result) { + context.result = result; + return runtime.findNext(pseudoCommand, context); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + } + + return pseudoCommand; + }); + + /** + * @param {Parser} parser + * @param {Runtime} runtime + * @param {Tokens} tokens + * @param {*} target + * @param {*} value + * @returns + */ + var makeSetter = function (parser, runtime, tokens, target, value) { + + var symbolWrite = target.type === "symbol"; + var attributeWrite = target.type === "attributeRef"; + var styleWrite = target.type === "styleRef"; + var arrayWrite = target.type === "arrayIndex"; + + if (!(attributeWrite || styleWrite || symbolWrite) && target.root == null) { + parser.raiseParseError(tokens, "Can only put directly into symbols, not references"); + } + + var rootElt = null; + var prop = null; + if (symbolWrite) { + // rootElt is null + } else if (attributeWrite || styleWrite) { + rootElt = parser.requireElement("implicitMeTarget", tokens); + var attribute = target; + } else if(arrayWrite) { + prop = target.firstIndex; + rootElt = target.root; + } else { + prop = target.prop ? target.prop.value : null; + var attribute = target.attribute; + rootElt = target.root; + } + + /** @type {ASTNode} */ + var setCmd = { + target: target, + symbolWrite: symbolWrite, + value: value, + args: [rootElt, prop, value], + op: function (context, root, prop, valueToSet) { + if (symbolWrite) { + runtime.setSymbol(target.name, context, target.scope, valueToSet); + } else { + runtime.nullCheck(root, rootElt); + if (arrayWrite) { + root[prop] = valueToSet; + } else { + runtime.implicitLoop(root, function (elt) { + if (attribute) { + if (attribute.type === "attributeRef") { + if (valueToSet == null) { + elt.removeAttribute(attribute.name); + } else { + elt.setAttribute(attribute.name, valueToSet); + } + } else { + elt.style[attribute.name] = valueToSet; + } + } else { + elt[prop] = valueToSet; + } + }); + } + } + return runtime.findNext(this, context); + }, + }; + return setCmd; + }; + + parser.addCommand("default", function (parser, runtime, tokens) { + if (!tokens.matchToken("default")) return; + var target = parser.requireElement("assignableExpression", tokens); + tokens.requireToken("to"); + + var value = parser.requireElement("expression", tokens); + + /** @type {ASTNode} */ + var setter = makeSetter(parser, runtime, tokens, target, value); + var defaultCmd = { + target: target, + value: value, + setter: setter, + args: [target], + op: function (context, target) { + if (target) { + return runtime.findNext(this, context); + } else { + return setter; + } + }, + }; + setter.parent = defaultCmd; + return defaultCmd; + }); + + parser.addCommand("set", function (parser, runtime, tokens) { + if (!tokens.matchToken("set")) return; + if (tokens.currentToken().type === "L_BRACE") { + var obj = parser.requireElement("objectLiteral", tokens); + tokens.requireToken("on"); + var target = parser.requireElement("expression", tokens); + + var command = { + objectLiteral: obj, + target: target, + args: [obj, target], + op: function (ctx, obj, target) { + Object.assign(target, obj); + return runtime.findNext(this, ctx); + }, + }; + return command; + } + + try { + tokens.pushFollow("to"); + var target = parser.requireElement("assignableExpression", tokens); + } finally { + tokens.popFollow(); + } + tokens.requireToken("to"); + var value = parser.requireElement("expression", tokens); + return makeSetter(parser, runtime, tokens, target, value); + }); + + parser.addCommand("if", function (parser, runtime, tokens) { + if (!tokens.matchToken("if")) return; + var expr = parser.requireElement("expression", tokens); + tokens.matchToken("then"); // optional 'then' + var trueBranch = parser.parseElement("commandList", tokens); + var nestedIfStmt = false; + if (tokens.matchToken("else") || tokens.matchToken("otherwise")) { + nestedIfStmt = tokens.peekToken("if"); + var falseBranch = parser.parseElement("commandList", tokens); + } + if (tokens.hasMore() && !nestedIfStmt) { + tokens.requireToken("end"); + } + + /** @type {ASTNode} */ + var ifCmd = { + expr: expr, + trueBranch: trueBranch, + falseBranch: falseBranch, + args: [expr], + op: function (context, exprValue) { + if (exprValue) { + return trueBranch; + } else if (falseBranch) { + return falseBranch; + } else { + return runtime.findNext(this, context); + } + }, + }; + parser.setParent(trueBranch, ifCmd); + parser.setParent(falseBranch, ifCmd); + return ifCmd; + }); + + var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) { + var innerStartToken = tokens.currentToken(); + var identifier; + if (tokens.matchToken("for") || startedWithForToken) { + var identifierToken = tokens.requireTokenType("IDENTIFIER"); + identifier = identifierToken.value; + tokens.requireToken("in"); + var expression = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("in")) { + identifier = "it"; + var expression = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("while")) { + var whileExpr = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("until")) { + var isUntil = true; + if (tokens.matchToken("event")) { + var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name"); + if (tokens.matchToken("from")) { + var on = parser.requireElement("expression", tokens); + } + } else { + var whileExpr = parser.requireElement("expression", tokens); + } + } else { + if (!parser.commandBoundary(tokens.currentToken()) && + tokens.currentToken().value !== 'forever') { + var times = parser.requireElement("expression", tokens); + tokens.requireToken("times"); + } else { + tokens.matchToken("forever"); // consume optional forever + var forever = true; + } + } + + if (tokens.matchToken("index")) { + var identifierToken = tokens.requireTokenType("IDENTIFIER"); + var indexIdentifier = identifierToken.value; + } + + var loop = parser.parseElement("commandList", tokens); + if (loop && evt) { + // if this is an event based loop, wait a tick at the end of the loop so that + // events have a chance to trigger in the loop condition o_O))) + var last = loop; + while (last.next) { + last = last.next; + } + var waitATick = { + type: "waitATick", + op: function () { + return new Promise(function (resolve) { + setTimeout(function () { + resolve(runtime.findNext(waitATick)); + }, 0); + }); + }, + }; + last.next = waitATick; + } + if (tokens.hasMore()) { + tokens.requireToken("end"); + } + + if (identifier == null) { + identifier = "_implicit_repeat_" + innerStartToken.start; + var slot = identifier; + } else { + var slot = identifier + "_" + innerStartToken.start; + } + + var repeatCmd = { + identifier: identifier, + indexIdentifier: indexIdentifier, + slot: slot, + expression: expression, + forever: forever, + times: times, + until: isUntil, + event: evt, + on: on, + whileExpr: whileExpr, + resolveNext: function () { + return this; + }, + loop: loop, + args: [whileExpr, times], + op: function (context, whileValue, times) { + var iteratorInfo = context.meta.iterators[slot]; + var keepLooping = false; + var loopVal = null; + if (this.forever) { + keepLooping = true; + } else if (this.until) { + if (evt) { + keepLooping = context.meta.iterators[slot].eventFired === false; + } else { + keepLooping = whileValue !== true; + } + } else if (whileExpr) { + keepLooping = whileValue; + } else if (times) { + keepLooping = iteratorInfo.index < times; + } else { + var nextValFromIterator = iteratorInfo.iterator.next(); + keepLooping = !nextValFromIterator.done; + loopVal = nextValFromIterator.value; + } + + if (keepLooping) { + if (iteratorInfo.value) { + context.result = context.locals[identifier] = loopVal; + } else { + context.result = iteratorInfo.index; + } + if (indexIdentifier) { + context.locals[indexIdentifier] = iteratorInfo.index; + } + iteratorInfo.index++; + return loop; + } else { + context.meta.iterators[slot] = null; + return runtime.findNext(this.parent, context); + } + }, + }; + parser.setParent(loop, repeatCmd); + var repeatInit = { + name: "repeatInit", + args: [expression, evt, on], + op: function (context, value, event, on) { + var iteratorInfo = { + index: 0, + value: value, + eventFired: false, + }; + context.meta.iterators[slot] = iteratorInfo; + if (value && value[Symbol.iterator]) { + iteratorInfo.iterator = value[Symbol.iterator](); + } + if (evt) { + var target = on || context.me; + target.addEventListener( + event, + function (e) { + context.meta.iterators[slot].eventFired = true; + }, + { once: true } + ); + } + return repeatCmd; // continue to loop + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + parser.setParent(repeatCmd, repeatInit); + return repeatInit; + }; + + parser.addCommand("repeat", function (parser, runtime, tokens) { + if (tokens.matchToken("repeat")) { + return parseRepeatExpression(parser, tokens, runtime, false); + } + }); + + parser.addCommand("for", function (parser, runtime, tokens) { + if (tokens.matchToken("for")) { + return parseRepeatExpression(parser, tokens, runtime, true); + } + }); + + parser.addCommand("continue", function (parser, runtime, tokens) { + + if (!tokens.matchToken("continue")) return; + + var command = { + op: function (context) { + + // scan for the closest repeat statement + for (var parent = this.parent ; true ; parent = parent.parent) { + + if (parent == undefined) { + parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.") + } + if (parent.loop != undefined) { + return parent.resolveNext(context) + } + } + } + }; + return command; + }); + + parser.addCommand("break", function (parser, runtime, tokens) { + + if (!tokens.matchToken("break")) return; + + var command = { + op: function (context) { + + // scan for the closest repeat statement + for (var parent = this.parent ; true ; parent = parent.parent) { + + if (parent == undefined) { + parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.") + } + if (parent.loop != undefined) { + return runtime.findNext(parent.parent, context); + } + } + } + }; + return command; + }); + + parser.addGrammarElement("stringLike", function (parser, runtime, tokens) { + return parser.parseAnyOf(["string", "nakedString"], tokens); + }); + + parser.addCommand("append", function (parser, runtime, tokens) { + if (!tokens.matchToken("append")) return; + var targetExpr = null; + + var value = parser.requireElement("expression", tokens); + + /** @type {ASTNode} */ + var implicitResultSymbol = { + type: "symbol", + evaluate: function (context) { + return runtime.resolveSymbol("result", context); + }, + }; + + if (tokens.matchToken("to")) { + targetExpr = parser.requireElement("expression", tokens); + } else { + targetExpr = implicitResultSymbol; + } + + var setter = null; + if (targetExpr.type === "symbol" || targetExpr.type === "attributeRef" || targetExpr.root != null) { + setter = makeSetter(parser, runtime, tokens, targetExpr, implicitResultSymbol); + } + + var command = { + value: value, + target: targetExpr, + args: [targetExpr, value], + op: function (context, target, value) { + if (Array.isArray(target)) { + target.push(value); + return runtime.findNext(this, context); + } else if (target instanceof Element) { + target.innerHTML += value; + return runtime.findNext(this, context); + } else if(setter) { + context.result = (target || "") + value; + return setter; + } else { + throw Error("Unable to append a value!") + } + }, + execute: function (context) { + return runtime.unifiedExec(this, context/*, value, target*/); + }, + }; + + if (setter != null) { + setter.parent = command; + } + + return command; + }); + + function parsePickRange(parser, runtime, tokens) { + tokens.matchToken("at") || tokens.matchToken("from"); + const rv = { includeStart: true, includeEnd: false } + + rv.from = tokens.matchToken("start") ? 0 : parser.requireElement("expression", tokens) + + if (tokens.matchToken("to") || tokens.matchOpToken("..")) { + if (tokens.matchToken("end")) { + rv.toEnd = true; + } else { + rv.to = parser.requireElement("expression", tokens); + } + } + + if (tokens.matchToken("inclusive")) rv.includeEnd = true; + else if (tokens.matchToken("exclusive")) rv.includeStart = false; + + return rv; + } + + class RegExpIterator { + constructor(re, str) { + this.re = re; + this.str = str; + } + + next() { + const match = this.re.exec(this.str); + if (match === null) return { done: true }; + else return { value: match }; + } + } + + class RegExpIterable { + constructor(re, flags, str) { + this.re = re; + this.flags = flags; + this.str = str; + } + + [Symbol.iterator]() { + return new RegExpIterator(new RegExp(this.re, this.flags), this.str); + } + } + + parser.addCommand("pick", (parser, runtime, tokens) => { + if (!tokens.matchToken("pick")) return; + + tokens.matchToken("the"); + + if (tokens.matchToken("item") || tokens.matchToken("items") + || tokens.matchToken("character") || tokens.matchToken("characters")) { + const range = parsePickRange(parser, runtime, tokens); + + tokens.requireToken("from"); + const root = parser.requireElement("expression", tokens); + + return { + args: [root, range.from, range.to], + op(ctx, root, from, to) { + if (range.toEnd) to = root.length; + if (!range.includeStart) from++; + if (range.includeEnd) to++; + if (to == null || to == undefined) to = from + 1; + ctx.result = root.slice(from, to); + return runtime.findNext(this, ctx); + } + } + } + + if (tokens.matchToken("match")) { + tokens.matchToken("of"); + const re = parser.parseElement("expression", tokens); + let flags = "" + if (tokens.matchOpToken("|")) { + flags = tokens.requireToken("identifier").value; + } + + tokens.requireToken("from"); + const root = parser.parseElement("expression", tokens); + + return { + args: [root, re], + op(ctx, root, re) { + ctx.result = new RegExp(re, flags).exec(root); + return runtime.findNext(this, ctx); + } + } + } + + if (tokens.matchToken("matches")) { + tokens.matchToken("of"); + const re = parser.parseElement("expression", tokens); + let flags = "gu" + if (tokens.matchOpToken("|")) { + flags = 'g' + tokens.requireToken("identifier").value.replace('g', ''); + } + console.log('flags', flags) + + tokens.requireToken("from"); + const root = parser.parseElement("expression", tokens); + + return { + args: [root, re], + op(ctx, root, re) { + ctx.result = new RegExpIterable(re, flags, root); + return runtime.findNext(this, ctx); + } + } + } + }); + + parser.addCommand("increment", function (parser, runtime, tokens) { + if (!tokens.matchToken("increment")) return; + var amountExpr; + + // This is optional. Defaults to "result" + var target = parser.parseElement("assignableExpression", tokens); + + // This is optional. Defaults to 1. + if (tokens.matchToken("by")) { + amountExpr = parser.requireElement("expression", tokens); + } + + var implicitIncrementOp = { + type: "implicitIncrementOp", + target: target, + args: [target, amountExpr], + op: function (context, targetValue, amount) { + targetValue = targetValue ? parseFloat(targetValue) : 0; + amount = amountExpr ? parseFloat(amount) : 1; + var newValue = targetValue + amount; + context.result = newValue; + return newValue; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + } + }; + + return makeSetter(parser, runtime, tokens, target, implicitIncrementOp); + }); + + parser.addCommand("decrement", function (parser, runtime, tokens) { + if (!tokens.matchToken("decrement")) return; + var amountExpr; + + // This is optional. Defaults to "result" + var target = parser.parseElement("assignableExpression", tokens); + + // This is optional. Defaults to 1. + if (tokens.matchToken("by")) { + amountExpr = parser.requireElement("expression", tokens); + } + + var implicitDecrementOp = { + type: "implicitDecrementOp", + target: target, + args: [target, amountExpr], + op: function (context, targetValue, amount) { + targetValue = targetValue ? parseFloat(targetValue) : 0; + amount = amountExpr ? parseFloat(amount) : 1; + var newValue = targetValue - amount; + context.result = newValue; + return newValue; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + } + }; + + return makeSetter(parser, runtime, tokens, target, implicitDecrementOp); + }); + + function parseConversionInfo(tokens, parser) { + var type = "text"; + var conversion; + tokens.matchToken("a") || tokens.matchToken("an"); + if (tokens.matchToken("json") || tokens.matchToken("Object")) { + type = "json"; + } else if (tokens.matchToken("response")) { + type = "response"; + } else if (tokens.matchToken("html")) { + type = "html"; + } else if (tokens.matchToken("text")) { + // default, ignore + } else { + conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); + } + return {type, conversion}; + } + + parser.addCommand("fetch", function (parser, runtime, tokens) { + if (!tokens.matchToken("fetch")) return; + var url = parser.requireElement("stringLike", tokens); + + if (tokens.matchToken("as")) { + var conversionInfo = parseConversionInfo(tokens, parser); + } + + if (tokens.matchToken("with") && tokens.currentToken().value !== "{") { + var args = parser.parseElement("nakedNamedArgumentList", tokens); + } else { + var args = parser.parseElement("objectLiteral", tokens); + } + + if (conversionInfo == null && tokens.matchToken("as")) { + conversionInfo = parseConversionInfo(tokens, parser); + } + + var type = conversionInfo ? conversionInfo.type : "text"; + var conversion = conversionInfo ? conversionInfo.conversion : null + + /** @type {ASTNode} */ + var fetchCmd = { + url: url, + argExpressions: args, + args: [url, args], + op: function (context, url, args) { + var detail = args || {}; + detail["sender"] = context.me; + detail["headers"] = detail["headers"] || {} + var abortController = new AbortController(); + let abortListener = context.me.addEventListener('fetch:abort', function(){ + abortController.abort(); + }, {once: true}); + detail['signal'] = abortController.signal; + runtime.triggerEvent(context.me, "hyperscript:beforeFetch", detail); + runtime.triggerEvent(context.me, "fetch:beforeRequest", detail); + args = detail; + var finished = false; + if (args.timeout) { + setTimeout(function () { + if (!finished) { + abortController.abort(); + } + }, args.timeout); + } + return fetch(url, args) + .then(function (resp) { + let resultDetails = {response:resp}; + runtime.triggerEvent(context.me, "fetch:afterResponse", resultDetails); + resp = resultDetails.response; + + if (type === "response") { + context.result = resp; + runtime.triggerEvent(context.me, "fetch:afterRequest", {result:resp}); + finished = true; + return runtime.findNext(fetchCmd, context); + } + if (type === "json") { + return resp.json().then(function (result) { + context.result = result; + runtime.triggerEvent(context.me, "fetch:afterRequest", {result}); + finished = true; + return runtime.findNext(fetchCmd, context); + }); + } + return resp.text().then(function (result) { + if (conversion) result = runtime.convertValue(result, conversion); + + if (type === "html") result = runtime.convertValue(result, "Fragment"); + + context.result = result; + runtime.triggerEvent(context.me, "fetch:afterRequest", {result}); + finished = true; + return runtime.findNext(fetchCmd, context); + }); + }) + .catch(function (reason) { + runtime.triggerEvent(context.me, "fetch:error", { + reason: reason, + }); + throw reason; + }).finally(function(){ + context.me.removeEventListener('fetch:abort', abortListener); + }); + }, + }; + return fetchCmd; + }); + } + + function hyperscriptWebGrammar(parser) { + parser.addCommand("settle", function (parser, runtime, tokens) { + if (tokens.matchToken("settle")) { + if (!parser.commandBoundary(tokens.currentToken())) { + var onExpr = parser.requireElement("expression", tokens); + } else { + var onExpr = parser.requireElement("implicitMeTarget", tokens); + } + + var settleCommand = { + type: "settleCmd", + args: [onExpr], + op: function (context, on) { + runtime.nullCheck(on, onExpr); + var resolve = null; + var resolved = false; + var transitionStarted = false; + + var promise = new Promise(function (r) { + resolve = r; + }); + + // listen for a transition begin + on.addEventListener( + "transitionstart", + function () { + transitionStarted = true; + }, + { once: true } + ); + + // if no transition begins in 500ms, cancel + setTimeout(function () { + if (!transitionStarted && !resolved) { + resolve(runtime.findNext(settleCommand, context)); + } + }, 500); + + // continue on a transition emd + on.addEventListener( + "transitionend", + function () { + if (!resolved) { + resolve(runtime.findNext(settleCommand, context)); + } + }, + { once: true } + ); + return promise; + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + return settleCommand; + } + }); + + parser.addCommand("add", function (parser, runtime, tokens) { + if (tokens.matchToken("add")) { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + var cssDeclaration = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + cssDeclaration = parser.parseElement("styleLiteral", tokens); + if (cssDeclaration == null) { + parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); + } + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + + if (tokens.matchToken("to")) { + var toExpr = parser.requireElement("expression", tokens); + } else { + var toExpr = parser.requireElement("implicitMeTarget", tokens); + } + + if (tokens.matchToken("when")) { + if (cssDeclaration) { + parser.raiseParseError(tokens, "Only class and properties are supported with a when clause") + } + var when = parser.requireElement("expression", tokens); + } + + if (classRefs) { + return { + classRefs: classRefs, + to: toExpr, + args: [toExpr, classRefs], + op: function (context, to, classRefs) { + runtime.nullCheck(to, toExpr); + runtime.forEach(classRefs, function (classRef) { + runtime.implicitLoop(to, function (target) { + if (when) { + context.result = target; + let whenResult = runtime.evaluateNoPromise(when, context); + if (whenResult) { + if (target instanceof Element) target.classList.add(classRef.className); + } else { + if (target instanceof Element) target.classList.remove(classRef.className); + } + context.result = null; + } else { + if (target instanceof Element) target.classList.add(classRef.className); + } + }); + }); + return runtime.findNext(this, context); + }, + }; + } else if (attributeRef) { + return { + type: "addCmd", + attributeRef: attributeRef, + to: toExpr, + args: [toExpr], + op: function (context, to, attrRef) { + runtime.nullCheck(to, toExpr); + runtime.implicitLoop(to, function (target) { + if (when) { + context.result = target; + let whenResult = runtime.evaluateNoPromise(when, context); + if (whenResult) { + target.setAttribute(attributeRef.name, attributeRef.value); + } else { + target.removeAttribute(attributeRef.name); + } + context.result = null; + } else { + target.setAttribute(attributeRef.name, attributeRef.value); + } + }); + return runtime.findNext(this, context); + }, + execute: function (ctx) { + return runtime.unifiedExec(this, ctx); + }, + }; + } else { + return { + type: "addCmd", + cssDeclaration: cssDeclaration, + to: toExpr, + args: [toExpr, cssDeclaration], + op: function (context, to, css) { + runtime.nullCheck(to, toExpr); + runtime.implicitLoop(to, function (target) { + target.style.cssText += css; + }); + return runtime.findNext(this, context); + }, + execute: function (ctx) { + return runtime.unifiedExec(this, ctx); + }, + }; + } + } + }); + + parser.addGrammarElement("styleLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("{")) return; + + var stringParts = [""] + var exprs = [] + + while (tokens.hasMore()) { + if (tokens.matchOpToken("\\")) { + tokens.consumeToken(); + } else if (tokens.matchOpToken("}")) { + break; + } else if (tokens.matchToken("$")) { + var opencurly = tokens.matchOpToken("{"); + var expr = parser.parseElement("expression", tokens); + if (opencurly) tokens.requireOpToken("}"); + + exprs.push(expr) + stringParts.push("") + } else { + var tok = tokens.consumeToken(); + stringParts[stringParts.length-1] += tokens.source.substring(tok.start, tok.end); + } + + stringParts[stringParts.length-1] += tokens.lastWhitespace(); + } + + return { + type: "styleLiteral", + args: [exprs], + op: function (ctx, exprs) { + var rv = ""; + + stringParts.forEach(function (part, idx) { + rv += part; + if (idx in exprs) rv += exprs[idx]; + }); + + return rv; + }, + evaluate: function(ctx) { + return runtime.unifiedEval(this, ctx); + } + } + }) + + parser.addCommand("remove", function (parser, runtime, tokens) { + if (tokens.matchToken("remove")) { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + var elementExpr = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + elementExpr = parser.parseElement("expression", tokens); + if (elementExpr == null) { + parser.raiseParseError( + tokens, + "Expected either a class reference, attribute expression or value expression" + ); + } + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + + if (tokens.matchToken("from")) { + var fromExpr = parser.requireElement("expression", tokens); + } else { + if (elementExpr == null) { + var fromExpr = parser.requireElement("implicitMeTarget", tokens); + } + } + + if (elementExpr) { + return { + elementExpr: elementExpr, + from: fromExpr, + args: [elementExpr, fromExpr], + op: function (context, element, from) { + runtime.nullCheck(element, elementExpr); + runtime.implicitLoop(element, function (target) { + if (target.parentElement && (from == null || from.contains(target))) { + target.parentElement.removeChild(target); + } + }); + return runtime.findNext(this, context); + }, + }; + } else { + return { + classRefs: classRefs, + attributeRef: attributeRef, + elementExpr: elementExpr, + from: fromExpr, + args: [classRefs, fromExpr], + op: function (context, classRefs, from) { + runtime.nullCheck(from, fromExpr); + if (classRefs) { + runtime.forEach(classRefs, function (classRef) { + runtime.implicitLoop(from, function (target) { + target.classList.remove(classRef.className); + }); + }); + } else { + runtime.implicitLoop(from, function (target) { + target.removeAttribute(attributeRef.name); + }); + } + return runtime.findNext(this, context); + }, + }; + } + } + }); + + parser.addCommand("toggle", function (parser, runtime, tokens) { + if (tokens.matchToken("toggle")) { + tokens.matchAnyToken("the", "my"); + if (tokens.currentToken().type === "STYLE_REF") { + let styleRef = tokens.consumeToken(); + var name = styleRef.value.substr(1); + var visibility = true; + var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); + if (tokens.matchToken("of")) { + tokens.pushFollow("with"); + try { + var onExpr = parser.requireElement("expression", tokens); + } finally { + tokens.popFollow(); + } + } else { + var onExpr = parser.requireElement("implicitMeTarget", tokens); + } + } else if (tokens.matchToken("between")) { + var between = true; + var classRef = parser.parseElement("classRef", tokens); + tokens.requireToken("and"); + var classRef2 = parser.requireElement("classRef", tokens); + } else { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + } + + if (visibility !== true) { + if (tokens.matchToken("on")) { + var onExpr = parser.requireElement("expression", tokens); + } else { + var onExpr = parser.requireElement("implicitMeTarget", tokens); + } + } + + if (tokens.matchToken("for")) { + var time = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("until")) { + var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name"); + if (tokens.matchToken("from")) { + var from = parser.requireElement("expression", tokens); + } + } + + var toggleCmd = { + classRef: classRef, + classRef2: classRef2, + classRefs: classRefs, + attributeRef: attributeRef, + on: onExpr, + time: time, + evt: evt, + from: from, + toggle: function (on, classRef, classRef2, classRefs) { + runtime.nullCheck(on, onExpr); + if (visibility) { + runtime.implicitLoop(on, function (target) { + hideShowStrategy("toggle", target); + }); + } else if (between) { + runtime.implicitLoop(on, function (target) { + if (target.classList.contains(classRef.className)) { + target.classList.remove(classRef.className); + target.classList.add(classRef2.className); + } else { + target.classList.add(classRef.className); + target.classList.remove(classRef2.className); + } + }); + } else if (classRefs) { + runtime.forEach(classRefs, function (classRef) { + runtime.implicitLoop(on, function (target) { + target.classList.toggle(classRef.className); + }); + }); + } else { + runtime.forEach(on, function (target) { + if (target.hasAttribute(attributeRef.name)) { + target.removeAttribute(attributeRef.name); + } else { + target.setAttribute(attributeRef.name, attributeRef.value); + } + }); + } + }, + args: [onExpr, time, evt, from, classRef, classRef2, classRefs], + op: function (context, on, time, evt, from, classRef, classRef2, classRefs) { + if (time) { + return new Promise(function (resolve) { + toggleCmd.toggle(on, classRef, classRef2, classRefs); + setTimeout(function () { + toggleCmd.toggle(on, classRef, classRef2, classRefs); + resolve(runtime.findNext(toggleCmd, context)); + }, time); + }); + } else if (evt) { + return new Promise(function (resolve) { + var target = from || context.me; + target.addEventListener( + evt, + function () { + toggleCmd.toggle(on, classRef, classRef2, classRefs); + resolve(runtime.findNext(toggleCmd, context)); + }, + { once: true } + ); + toggleCmd.toggle(on, classRef, classRef2, classRefs); + }); + } else { + this.toggle(on, classRef, classRef2, classRefs); + return runtime.findNext(toggleCmd, context); + } + }, + }; + return toggleCmd; + } + }); + + var HIDE_SHOW_STRATEGIES = { + display: function (op, element, arg) { + if (arg) { + element.style.display = arg; + } else if (op === "toggle") { + if (getComputedStyle(element).display === "none") { + HIDE_SHOW_STRATEGIES.display("show", element, arg); + } else { + HIDE_SHOW_STRATEGIES.display("hide", element, arg); + } + } else if (op === "hide") { + const internalData = parser.runtime.getInternalData(element); + if (internalData.originalDisplay == null) { + internalData.originalDisplay = element.style.display; + } + element.style.display = "none"; + } else { + const internalData = parser.runtime.getInternalData(element); + if (internalData.originalDisplay && internalData.originalDisplay !== 'none') { + element.style.display = internalData.originalDisplay; + } else { + element.style.removeProperty('display'); + } + } + }, + visibility: function (op, element, arg) { + if (arg) { + element.style.visibility = arg; + } else if (op === "toggle") { + if (getComputedStyle(element).visibility === "hidden") { + HIDE_SHOW_STRATEGIES.visibility("show", element, arg); + } else { + HIDE_SHOW_STRATEGIES.visibility("hide", element, arg); + } + } else if (op === "hide") { + element.style.visibility = "hidden"; + } else { + element.style.visibility = "visible"; + } + }, + opacity: function (op, element, arg) { + if (arg) { + element.style.opacity = arg; + } else if (op === "toggle") { + if (getComputedStyle(element).opacity === "0") { + HIDE_SHOW_STRATEGIES.opacity("show", element, arg); + } else { + HIDE_SHOW_STRATEGIES.opacity("hide", element, arg); + } + } else if (op === "hide") { + element.style.opacity = "0"; + } else { + element.style.opacity = "1"; + } + }, + }; + + var parseShowHideTarget = function (parser, runtime, tokens) { + var target; + var currentTokenValue = tokens.currentToken(); + if (currentTokenValue.value === "when" || currentTokenValue.value === "with" || parser.commandBoundary(currentTokenValue)) { + target = parser.parseElement("implicitMeTarget", tokens); + } else { + target = parser.parseElement("expression", tokens); + } + return target; + }; + + var resolveHideShowStrategy = function (parser, tokens, name) { + var configDefault = config.defaultHideShowStrategy; + var strategies = HIDE_SHOW_STRATEGIES; + if (config.hideShowStrategies) { + strategies = Object.assign(strategies, config.hideShowStrategies); // merge in user provided strategies + } + name = name || configDefault || "display"; + var value = strategies[name]; + if (value == null) { + parser.raiseParseError(tokens, "Unknown show/hide strategy : " + name); + } + return value; + }; + + parser.addCommand("hide", function (parser, runtime, tokens) { + if (tokens.matchToken("hide")) { + var targetExpr = parseShowHideTarget(parser, runtime, tokens); + + var name = null; + if (tokens.matchToken("with")) { + name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value; + if (name.indexOf("*") === 0) { + name = name.substr(1); + } + } + var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); + + return { + target: targetExpr, + args: [targetExpr], + op: function (ctx, target) { + runtime.nullCheck(target, targetExpr); + runtime.implicitLoop(target, function (elt) { + hideShowStrategy("hide", elt); + }); + return runtime.findNext(this, ctx); + }, + }; + } + }); + + parser.addCommand("show", function (parser, runtime, tokens) { + if (tokens.matchToken("show")) { + var targetExpr = parseShowHideTarget(parser, runtime, tokens); + + var name = null; + if (tokens.matchToken("with")) { + name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value; + if (name.indexOf("*") === 0) { + name = name.substr(1); + } + } + var arg = null; + if (tokens.matchOpToken(":")) { + var tokenArr = tokens.consumeUntilWhitespace(); + tokens.matchTokenType("WHITESPACE"); + arg = tokenArr + .map(function (t) { + return t.value; + }) + .join(""); + } + + if (tokens.matchToken("when")) { + var when = parser.requireElement("expression", tokens); + } + + var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); + + return { + target: targetExpr, + when: when, + args: [targetExpr], + op: function (ctx, target) { + runtime.nullCheck(target, targetExpr); + runtime.implicitLoop(target, function (elt) { + if (when) { + ctx.result = elt; + let whenResult = runtime.evaluateNoPromise(when, ctx); + if (whenResult) { + hideShowStrategy("show", elt, arg); + } else { + hideShowStrategy("hide", elt); + } + ctx.result = null; + } else { + hideShowStrategy("show", elt, arg); + } + }); + return runtime.findNext(this, ctx); + }, + }; + } + }); + + parser.addCommand("take", function (parser, runtime, tokens) { + if (tokens.matchToken("take")) { + var classRef = parser.parseElement("classRef", tokens); + + var attributeRef = null; + var replacementValue = null; + + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); + } + + if (tokens.matchToken("with")) { + replacementValue = parser.requireElement("expression", tokens); + } + } + + if (tokens.matchToken("from")) { + var fromExpr = parser.requireElement("expression", tokens); + } else { + var fromExpr = classRef; + } + + if (tokens.matchToken("for")) { + var forExpr = parser.requireElement("expression", tokens); + } else { + var forExpr = parser.requireElement("implicitMeTarget", tokens); + } + + if (classRef) { + var takeCmd = { + classRef: classRef, + from: fromExpr, + forElt: forExpr, + args: [classRef, fromExpr, forExpr], + op: function (context, eltColl, from, forElt) { + runtime.nullCheck(from, fromExpr); + runtime.nullCheck(forElt, forExpr); + var clazz = eltColl.className; + runtime.implicitLoop(from, function (target) { + target.classList.remove(clazz); + }); + runtime.implicitLoop(forElt, function (target) { + target.classList.add(clazz); + }); + return runtime.findNext(this, context); + }, + }; + return takeCmd; + } else { + var takeCmd = { + attributeRef: attributeRef, + from: fromExpr, + forElt: forExpr, + args: [fromExpr, forExpr, replacementValue], + op: function (context, from, forElt, replacementValue) { + runtime.nullCheck(from, fromExpr); + runtime.nullCheck(forElt, forExpr); + runtime.implicitLoop(from, function (target) { + if (!replacementValue) { + target.removeAttribute(attributeRef.name); + } else { + target.setAttribute(attributeRef.name, replacementValue) + } + }); + runtime.implicitLoop(forElt, function (target) { + target.setAttribute(attributeRef.name, attributeRef.value || "") + }); + return runtime.findNext(this, context); + }, + }; + return takeCmd; + } + } + }); + + function putInto(runtime, context, prop, valueToPut) { + if (prop != null) { + var value = runtime.resolveSymbol(prop, context); + } else { + var value = context; + } + if (value instanceof Element || value instanceof HTMLDocument) { + while (value.firstChild) value.removeChild(value.firstChild); + value.append(parser.runtime.convertValue(valueToPut, "Fragment")); + runtime.processNode(value); + } else { + if (prop != null) { + runtime.setSymbol(prop, context, null, valueToPut); + } else { + throw "Don't know how to put a value into " + typeof context; + } + } + } + + parser.addCommand("put", function (parser, runtime, tokens) { + if (tokens.matchToken("put")) { + var value = parser.requireElement("expression", tokens); + + var operationToken = tokens.matchAnyToken("into", "before", "after"); + + if (operationToken == null && tokens.matchToken("at")) { + tokens.matchToken("the"); // optional "the" + operationToken = tokens.matchAnyToken("start", "end"); + tokens.requireToken("of"); + } + + if (operationToken == null) { + parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'at start of', 'at end of', 'after'"); + } + var target = parser.requireElement("expression", tokens); + + var operation = operationToken.value; + + var arrayIndex = false; + var symbolWrite = false; + var rootExpr = null; + var prop = null; + + if (target.type === "arrayIndex" && operation === "into") { + arrayIndex = true; + prop = target.prop; + rootExpr = target.root; + } else if (target.prop && target.root && operation === "into") { + prop = target.prop.value; + rootExpr = target.root; + } else if (target.type === "symbol" && operation === "into") { + symbolWrite = true; + prop = target.name; + } else if (target.type === "attributeRef" && operation === "into") { + var attributeWrite = true; + prop = target.name; + rootExpr = parser.requireElement("implicitMeTarget", tokens); + } else if (target.type === "styleRef" && operation === "into") { + var styleWrite = true; + prop = target.name; + rootExpr = parser.requireElement("implicitMeTarget", tokens); + } else if (target.attribute && operation === "into") { + var attributeWrite = target.attribute.type === "attributeRef"; + var styleWrite = target.attribute.type === "styleRef"; + prop = target.attribute.name; + rootExpr = target.root; + } else { + rootExpr = target; + } + + var putCmd = { + target: target, + operation: operation, + symbolWrite: symbolWrite, + value: value, + args: [rootExpr, prop, value], + op: function (context, root, prop, valueToPut) { + if (symbolWrite) { + putInto(runtime, context, prop, valueToPut); + } else { + runtime.nullCheck(root, rootExpr); + if (operation === "into") { + if (attributeWrite) { + runtime.implicitLoop(root, function (elt) { + elt.setAttribute(prop, valueToPut); + }); + } else if (styleWrite) { + runtime.implicitLoop(root, function (elt) { + elt.style[prop] = valueToPut; + }); + } else if (arrayIndex) { + root[prop] = valueToPut; + } else { + runtime.implicitLoop(root, function (elt) { + putInto(runtime, elt, prop, valueToPut); + }); + } + } else { + var op = + operation === "before" + ? Element.prototype.before + : operation === "after" + ? Element.prototype.after + : operation === "start" + ? Element.prototype.prepend + : operation === "end" + ? Element.prototype.append + : Element.prototype.append; // unreachable + + runtime.implicitLoop(root, function (elt) { + op.call( + elt, + valueToPut instanceof Node + ? valueToPut + : runtime.convertValue(valueToPut, "Fragment") + ); + // process any new content + if (elt.parentElement) { + runtime.processNode(elt.parentElement); + } else { + runtime.processNode(elt); + } + }); + } + } + return runtime.findNext(this, context); + }, + }; + return putCmd; + } + }); + + function parsePseudopossessiveTarget(parser, runtime, tokens) { + var targets; + if ( + tokens.matchToken("the") || + tokens.matchToken("element") || + tokens.matchToken("elements") || + tokens.currentToken().type === "CLASS_REF" || + tokens.currentToken().type === "ID_REF" || + (tokens.currentToken().op && tokens.currentToken().value === "<") + ) { + parser.possessivesDisabled = true; + try { + targets = parser.parseElement("expression", tokens); + } finally { + delete parser.possessivesDisabled; + } + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.currentToken().value === "its") { + var identifier = tokens.matchToken("its"); + targets = { + type: "pseudopossessiveIts", + token: identifier, + name: identifier.value, + evaluate: function (context) { + return runtime.resolveSymbol("it", context); + }, + }; + } else { + tokens.matchToken("my") || tokens.matchToken("me"); // consume optional 'my' + targets = parser.parseElement("implicitMeTarget", tokens); + } + return targets; + } + + parser.addCommand("transition", function (parser, runtime, tokens) { + if (tokens.matchToken("transition")) { + var targetsExpr = parsePseudopossessiveTarget(parser, runtime, tokens); + + var properties = []; + var from = []; + var to = []; + var currentToken = tokens.currentToken(); + while ( + !parser.commandBoundary(currentToken) && + currentToken.value !== "over" && + currentToken.value !== "using" + ) { + if (tokens.currentToken().type === "STYLE_REF") { + let styleRef = tokens.consumeToken(); + let styleProp = styleRef.value.substr(1); + properties.push({ + type: "styleRefValue", + evaluate: function () { + return styleProp; + }, + }); + } else { + properties.push(parser.requireElement("stringLike", tokens)); + } + + if (tokens.matchToken("from")) { + from.push(parser.requireElement("expression", tokens)); + } else { + from.push(null); + } + tokens.requireToken("to"); + if (tokens.matchToken("initial")) { + to.push({ + type: "initial_literal", + evaluate : function(){ + return "initial"; + } + }); + } else { + to.push(parser.requireElement("expression", tokens)); + } + currentToken = tokens.currentToken(); + } + if (tokens.matchToken("over")) { + var over = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("using")) { + var using = parser.requireElement("expression", tokens); + } + + var transition = { + to: to, + args: [targetsExpr, properties, from, to, using, over], + op: function (context, targets, properties, from, to, using, over) { + runtime.nullCheck(targets, targetsExpr); + var promises = []; + runtime.implicitLoop(targets, function (target) { + var promise = new Promise(function (resolve, reject) { + var initialTransition = target.style.transition; + if (over) { + target.style.transition = "all " + over + "ms ease-in"; + } else if (using) { + target.style.transition = using; + } else { + target.style.transition = config.defaultTransition; + } + var internalData = runtime.getInternalData(target); + var computedStyles = getComputedStyle(target); + + var initialStyles = {}; + for (var i = 0; i < computedStyles.length; i++) { + var name = computedStyles[i]; + var initialValue = computedStyles[name]; + initialStyles[name] = initialValue; + } + + // store intitial values + if (!internalData.initalStyles) { + internalData.initalStyles = initialStyles; + } + + for (var i = 0; i < properties.length; i++) { + var property = properties[i]; + var fromVal = from[i]; + if (fromVal === "computed" || fromVal == null) { + target.style[property] = initialStyles[property]; + } else { + target.style[property] = fromVal; + } + } + //console.log("transition started", transition); + + var transitionStarted = false; + var resolved = false; + + target.addEventListener( + "transitionend", + function () { + if (!resolved) { + //console.log("transition ended", transition); + target.style.transition = initialTransition; + resolved = true; + resolve(); + } + }, + { once: true } + ); + + target.addEventListener( + "transitionstart", + function () { + transitionStarted = true; + }, + { once: true } + ); + + // it no transition has started in 100ms, continue + setTimeout(function () { + if (!resolved && !transitionStarted) { + //console.log("transition ended", transition); + target.style.transition = initialTransition; + resolved = true; + resolve(); + } + }, 100); + + setTimeout(function () { + var autoProps = []; + for (var i = 0; i < properties.length; i++) { + var property = properties[i]; + var toVal = to[i]; + if (toVal === "initial") { + var propertyValue = internalData.initalStyles[property]; + target.style[property] = propertyValue; + } else { + target.style[property] = toVal; + } + //console.log("set", property, "to", target.style[property], "on", target, "value passed in : ", toVal); + } + }, 0); + }); + promises.push(promise); + }); + return Promise.all(promises).then(function () { + return runtime.findNext(transition, context); + }); + }, + }; + return transition; + } + }); + + parser.addCommand("measure", function (parser, runtime, tokens) { + if (!tokens.matchToken("measure")) return; + + var targetExpr = parsePseudopossessiveTarget(parser, runtime, tokens); + + var propsToMeasure = []; + if (!parser.commandBoundary(tokens.currentToken())) + do { + propsToMeasure.push(tokens.matchTokenType("IDENTIFIER").value); + } while (tokens.matchOpToken(",")); + + return { + properties: propsToMeasure, + args: [targetExpr], + op: function (ctx, target) { + runtime.nullCheck(target, targetExpr); + if (0 in target) target = target[0]; // not measuring multiple elts + var rect = target.getBoundingClientRect(); + var scroll = { + top: target.scrollTop, + left: target.scrollLeft, + topMax: target.scrollTopMax, + leftMax: target.scrollLeftMax, + height: target.scrollHeight, + width: target.scrollWidth, + }; + + ctx.result = { + x: rect.x, + y: rect.y, + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + width: rect.width, + height: rect.height, + bounds: rect, + + scrollLeft: scroll.left, + scrollTop: scroll.top, + scrollLeftMax: scroll.leftMax, + scrollTopMax: scroll.topMax, + scrollWidth: scroll.width, + scrollHeight: scroll.height, + scroll: scroll, + }; + + runtime.forEach(propsToMeasure, function (prop) { + if (prop in ctx.result) ctx.locals[prop] = ctx.result[prop]; + else throw "No such measurement as " + prop; + }); + + return runtime.findNext(this, ctx); + }, + }; + }); + + parser.addLeafExpression("closestExpr", function (parser, runtime, tokens) { + if (tokens.matchToken("closest")) { + if (tokens.matchToken("parent")) { + var parentSearch = true; + } + + var css = null; + if (tokens.currentToken().type === "ATTRIBUTE_REF") { + var attributeRef = parser.requireElement("attributeRefAccess", tokens, null); + css = "[" + attributeRef.attribute.name + "]"; + } + + if (css == null) { + var expr = parser.requireElement("expression", tokens); + if (expr.css == null) { + parser.raiseParseError(tokens, "Expected a CSS expression"); + } else { + css = expr.css; + } + } + + if (tokens.matchToken("to")) { + var to = parser.parseElement("expression", tokens); + } else { + var to = parser.parseElement("implicitMeTarget", tokens); + } + + var closestExpr = { + type: "closestExpr", + parentSearch: parentSearch, + expr: expr, + css: css, + to: to, + args: [to], + op: function (ctx, to) { + if (to == null) { + return null; + } else { + let result = []; + runtime.implicitLoop(to, function(to){ + if (parentSearch) { + result.push(to.parentElement ? to.parentElement.closest(css) : null); + } else { + result.push(to.closest(css)); + } + }) + if (runtime.shouldAutoIterate(to)) { + return result; + } else { + return result[0]; + } + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + + if (attributeRef) { + attributeRef.root = closestExpr; + attributeRef.args = [closestExpr]; + return attributeRef; + } else { + return closestExpr; + } + } + }); + + parser.addCommand("go", function (parser, runtime, tokens) { + if (tokens.matchToken("go")) { + if (tokens.matchToken("back")) { + var back = true; + } else { + tokens.matchToken("to"); + if (tokens.matchToken("url")) { + var target = parser.requireElement("stringLike", tokens); + var url = true; + if (tokens.matchToken("in")) { + tokens.requireToken("new"); + tokens.requireToken("window"); + var newWindow = true; + } + } else { + tokens.matchToken("the"); // optional the + var verticalPosition = tokens.matchAnyToken("top", "middle", "bottom"); + var horizontalPosition = tokens.matchAnyToken("left", "center", "right"); + if (verticalPosition || horizontalPosition) { + tokens.requireToken("of"); + } + var target = parser.requireElement("unaryExpression", tokens); + + var plusOrMinus = tokens.matchAnyOpToken("+", "-"); + if (plusOrMinus) { + tokens.pushFollow("px"); + try { + var offset = parser.requireElement("expression", tokens); + } finally { + tokens.popFollow(); + } + } + tokens.matchToken("px"); // optional px + + var smoothness = tokens.matchAnyToken("smoothly", "instantly"); + + var scrollOptions = {}; + if (verticalPosition) { + if (verticalPosition.value === "top") { + scrollOptions.block = "start"; + } else if (verticalPosition.value === "bottom") { + scrollOptions.block = "end"; + } else if (verticalPosition.value === "middle") { + scrollOptions.block = "center"; + } + } + + if (horizontalPosition) { + if (horizontalPosition.value === "left") { + scrollOptions.inline = "start"; + } else if (horizontalPosition.value === "center") { + scrollOptions.inline = "center"; + } else if (horizontalPosition.value === "right") { + scrollOptions.inline = "end"; + } + } + + if (smoothness) { + if (smoothness.value === "smoothly") { + scrollOptions.behavior = "smooth"; + } else if (smoothness.value === "instantly") { + scrollOptions.behavior = "instant"; + } + } + } + } + + var goCmd = { + target: target, + args: [target, offset], + op: function (ctx, to, offset) { + if (back) { + window.history.back(); + } else if (url) { + if (to) { + if (newWindow) { + window.open(to); + } else { + window.location.href = to; + } + } + } else { + runtime.implicitLoop(to, function (target) { + + if (target === window) { + target = document.body; + } + + if(plusOrMinus) { + // a top scroll w/ an offset of some sort + var boundingRect = target.getBoundingClientRect(); + let scrollShim = document.createElement('div'); + + if (plusOrMinus.value === "-") { + var finalOffset = -offset; + } else { + var finalOffset = - -offset; + } + + scrollShim.style.position = 'absolute'; + scrollShim.style.top = (boundingRect.x + finalOffset) + "px"; + scrollShim.style.left = (boundingRect.y + finalOffset) + "px"; + scrollShim.style.height = (boundingRect.height + (2 * finalOffset)) + "px"; + scrollShim.style.width = (boundingRect.width + (2 * finalOffset)) + "px"; + scrollShim.style.zIndex = "" + Number.MIN_SAFE_INTEGER; + scrollShim.style.opacity = "0"; + + document.body.appendChild(scrollShim); + setTimeout(function () { + document.body.removeChild(scrollShim); + }, 100); + + target = scrollShim; + } + + target.scrollIntoView(scrollOptions); + }); + } + return runtime.findNext(goCmd, ctx); + }, + }; + return goCmd; + } + }); + + config.conversions.dynamicResolvers.push(function (str, node) { + if (!(str === "Values" || str.indexOf("Values:") === 0)) { + return; + } + var conversion = str.split(":")[1]; + /** @type Object */ + var result = {}; + + var implicitLoop = parser.runtime.implicitLoop.bind(parser.runtime); + + implicitLoop(node, function (/** @type HTMLInputElement */ node) { + // Try to get a value directly from this node + var input = getInputInfo(node); + + if (input !== undefined) { + result[input.name] = input.value; + return; + } + + // Otherwise, try to query all child elements of this node that *should* contain values. + if (node.querySelectorAll != undefined) { + /** @type {NodeListOf} */ + var children = node.querySelectorAll("input,select,textarea"); + children.forEach(appendValue); + } + }); + + if (conversion) { + if (conversion === "JSON") { + return JSON.stringify(result); + } else if (conversion === "Form") { + /** @ts-ignore */ + // TODO: does this work with multiple inputs of the same name? + return new URLSearchParams(result).toString(); + } else { + throw "Unknown conversion: " + conversion; + } + } else { + return result; + } + + /** + * @param {HTMLInputElement} node + */ + function appendValue(node) { + var info = getInputInfo(node); + + if (info == undefined) { + return; + } + + // If there is no value already stored in this space. + if (result[info.name] == undefined) { + result[info.name] = info.value; + return; + } + + if (Array.isArray(result[info.name]) && Array.isArray(info.value)) { + result[info.name] = [].concat(result[info.name], info.value); + return; + } + } + + /** + * @param {HTMLInputElement} node + * @returns {{name:string, value:string | string[]} | undefined} + */ + function getInputInfo(node) { + try { + /** @type {{name: string, value: string | string[]}}*/ + var result = { + name: node.name, + value: node.value, + }; + + if (result.name == undefined || result.value == undefined) { + return undefined; + } + + if (node.type == "radio" && node.checked == false) { + return undefined; + } + + if (node.type == "checkbox") { + if (node.checked == false) { + result.value = undefined; + } else if (typeof result.value === "string") { + result.value = [result.value]; + } + } + + if (node.type == "select-multiple") { + /** @type {NodeListOf} */ + var selected = node.querySelectorAll("option[selected]"); + + result.value = []; + for (var index = 0; index < selected.length; index++) { + result.value.push(selected[index].value); + } + } + return result; + } catch (e) { + return undefined; + } + } + }); + + config.conversions["HTML"] = function (value) { + var toHTML = /** @returns {string}*/ function (/** @type any*/ value) { + if (value instanceof Array) { + return value + .map(function (item) { + return toHTML(item); + }) + .join(""); + } + + if (value instanceof HTMLElement) { + return value.outerHTML; + } + + if (value instanceof NodeList) { + var result = ""; + for (var i = 0; i < value.length; i++) { + var node = value[i]; + if (node instanceof HTMLElement) { + result += node.outerHTML; + } + } + return result; + } + + if (value.toString) { + return value.toString(); + } + + return ""; + }; + + return toHTML(value); + }; + + config.conversions["Fragment"] = function (val) { + var frag = document.createDocumentFragment(); + parser.runtime.implicitLoop(val, function (val) { + if (val instanceof Node) frag.append(val); + else { + var temp = document.createElement("template"); + temp.innerHTML = val; + frag.append(temp.content); + } + }); + return frag; + }; + } + + + // Public API + + const runtime_ = new Runtime(), lexer_ = runtime_.lexer, parser_ = runtime_.parser + + /** + * + * @param {string} src + * @param {Partial} [ctx] + */ + function run(src, ctx) { + return runtime_.evaluate(src, ctx) + } + + function browserInit() { + /** @type {HTMLScriptElement[]} */ + var scripts = Array.from(globalScope.document.querySelectorAll("script[type='text/hyperscript'][src]")) + Promise.all( + scripts.map(function (script) { + return fetch(script.src) + .then(function (res) { + return res.text(); + }); + }) + ) + .then(script_values => script_values.forEach(sc => _hyperscript(sc))) + .then(() => ready(function () { + mergeMetaConfig(); + runtime_.processNode(document.documentElement); + globalScope.document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) { + runtime_.processNode(evt.detail.elt); + }); + })); + + function ready(fn) { + if (document.readyState !== "loading") { + setTimeout(fn); + } else { + document.addEventListener("DOMContentLoaded", fn); + } + } + + function getMetaConfig() { + /** @type {HTMLMetaElement} */ + var element = document.querySelector('meta[name="htmx-config"]'); + if (element) { + return parseJSON(element.content); + } else { + return null; + } + } + + function mergeMetaConfig() { + var metaConfig = getMetaConfig(); + if (metaConfig) { + Object.assign(config, metaConfig); + } + } + } + + /** + * @typedef {Object} HyperscriptAPI + * + * @property {Object} config + * @property {string} config.attributes + * @property {string} config.defaultTransition + * @property {string} config.disableSelector + * @property {typeof conversions} config.conversions + * + * @property {Object} internals + * @property {Lexer} internals.lexer + * @property {typeof Lexer} internals.Lexer + * @property {Parser} internals.parser + * @property {typeof Parser} internals.Parser + * @property {Runtime} internals.runtime + * @property {typeof Runtime} internals.Runtime + * + * @property {typeof ElementCollection} ElementCollection + * + * @property {(keyword: string, definition: ParseRule) => void} addFeature + * @property {(keyword: string, definition: ParseRule) => void} addCommand + * @property {(keyword: string, definition: ParseRule) => void} addLeafExpression + * @property {(keyword: string, definition: ParseRule) => void} addIndirectExpression + * + * @property {(src: string, ctx?: Partial) => any} evaluate + * @property {(src: string) => ASTNode} parse + * @property {(node: Element) => void} processNode + * + * @property {() => void} browserInit + * + * + * @typedef {HyperscriptAPI & ((src: string, ctx?: Partial) => any)} Hyperscript + */ + + /** + * @type {Hyperscript} + */ + const _hyperscript = Object.assign( + run, + { + config, + + use(plugin) { plugin(_hyperscript) }, + + internals: { + lexer: lexer_, parser: parser_, runtime: runtime_, + Lexer, Tokens, Parser, Runtime, + }, + ElementCollection, + + addFeature: parser_.addFeature.bind(parser_), + addCommand: parser_.addCommand.bind(parser_), + addLeafExpression: parser_.addLeafExpression.bind(parser_), + addIndirectExpression: parser_.addIndirectExpression.bind(parser_), + + evaluate: runtime_.evaluate.bind(runtime_), + parse: runtime_.parse.bind(runtime_), + processNode: runtime_.processNode.bind(runtime_), + + browserInit, + } + ) + + return _hyperscript +}) diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html index eccf724..dc07a5f 100644 --- a/templates/interactive-posts.html +++ b/templates/interactive-posts.html @@ -16,12 +16,15 @@
{% include 'answer-post.html' %}
-
-

Upload IPS File

-
- - -
+
+
+ + + +
From d646b120d587102090d6931ac44d444d927cf27f Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:42:43 +1000 Subject: [PATCH 63/80] we can send now? --- static/src/_hyperscript.js | 7621 +----------------------------------- static/src/htmx.js | 3629 +---------------- 2 files changed, 2 insertions(+), 11248 deletions(-) diff --git a/static/src/_hyperscript.js b/static/src/_hyperscript.js index a41b6be..f0684cb 100644 --- a/static/src/_hyperscript.js +++ b/static/src/_hyperscript.js @@ -1,7620 +1 @@ -/** - * @typedef {Object} Hyperscript - */ - -(function (self, factory) { - const _hyperscript = factory(self) - - if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') { - module.exports = _hyperscript - } else { - self['_hyperscript'] = _hyperscript - if ('document' in self) self['_hyperscript'].browserInit() - } -})(typeof self !== 'undefined' ? self : this, (globalScope) => { - - 'use strict'; - - /** - * @type {Object} - * @property {DynamicConverter[]} dynamicResolvers - * - * @callback DynamicConverter - * @param {String} str - * @param {*} value - * @returns {*} - */ - const conversions = { - dynamicResolvers: [ - function(str, value){ - if (str === "Fixed") { - return Number(value).toFixed(); - } else if (str.indexOf("Fixed:") === 0) { - let num = str.split(":")[1]; - return Number(value).toFixed(parseInt(num)); - } - } - ], - String: function (val) { - if (val.toString) { - return val.toString(); - } else { - return "" + val; - } - }, - Int: function (val) { - return parseInt(val); - }, - Float: function (val) { - return parseFloat(val); - }, - Number: function (val) { - return Number(val); - }, - Date: function (val) { - return new Date(val); - }, - Array: function (val) { - return Array.from(val); - }, - JSON: function (val) { - return JSON.stringify(val); - }, - Object: function (val) { - if (val instanceof String) { - val = val.toString(); - } - if (typeof val === "string") { - return JSON.parse(val); - } else { - return Object.assign({}, val); - } - }, - } - - const config = { - attributes: "_, script, data-script", - defaultTransition: "all 500ms ease-in", - disableSelector: "[disable-scripting], [data-disable-scripting]", - hideShowStrategies: {}, - conversions, - } - - class Lexer { - static OP_TABLE = { - "+": "PLUS", - "-": "MINUS", - "*": "MULTIPLY", - "/": "DIVIDE", - ".": "PERIOD", - "..": "ELLIPSIS", - "\\": "BACKSLASH", - ":": "COLON", - "%": "PERCENT", - "|": "PIPE", - "!": "EXCLAMATION", - "?": "QUESTION", - "#": "POUND", - "&": "AMPERSAND", - $: "DOLLAR", - ";": "SEMI", - ",": "COMMA", - "(": "L_PAREN", - ")": "R_PAREN", - "<": "L_ANG", - ">": "R_ANG", - "<=": "LTE_ANG", - ">=": "GTE_ANG", - "==": "EQ", - "===": "EQQ", - "!=": "NEQ", - "!==": "NEQQ", - "{": "L_BRACE", - "}": "R_BRACE", - "[": "L_BRACKET", - "]": "R_BRACKET", - "=": "EQUALS", - }; - - /** - * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class. - * @param {string} c - * @returns boolean - */ - static isValidCSSClassChar(c) { - return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":"; - } - - /** - * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID - * @param {string} c - * @returns boolean - */ - static isValidCSSIDChar(c) { - return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":"; - } - - /** - * isWhitespace returns `true` if the provided character is whitespace. - * @param {string} c - * @returns boolean - */ - static isWhitespace(c) { - return c === " " || c === "\t" || Lexer.isNewline(c); - } - - /** - * positionString returns a string representation of a Token's line and column details. - * @param {Token} token - * @returns string - */ - static positionString(token) { - return "[Line: " + token.line + ", Column: " + token.column + "]"; - } - - /** - * isNewline returns `true` if the provided character is a carrage return or newline - * @param {string} c - * @returns boolean - */ - static isNewline(c) { - return c === "\r" || c === "\n"; - } - - /** - * isNumeric returns `true` if the provided character is a number (0-9) - * @param {string} c - * @returns boolean - */ - static isNumeric(c) { - return c >= "0" && c <= "9"; - } - - /** - * isAlpha returns `true` if the provided character is a letter in the alphabet - * @param {string} c - * @returns boolean - */ - static isAlpha(c) { - return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z"); - } - - /** - * @param {string} c - * @param {boolean} [dollarIsOp] - * @returns boolean - */ - static isIdentifierChar(c, dollarIsOp) { - return c === "_" || c === "$"; - } - - /** - * @param {string} c - * @returns boolean - */ - static isReservedChar(c) { - return c === "`" || c === "^"; - } - - /** - * @param {Token[]} tokens - * @returns {boolean} - */ - static isValidSingleQuoteStringStart(tokens) { - if (tokens.length > 0) { - var previousToken = tokens[tokens.length - 1]; - if ( - previousToken.type === "IDENTIFIER" || - previousToken.type === "CLASS_REF" || - previousToken.type === "ID_REF" - ) { - return false; - } - if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) { - return false; - } - } - return true; - } - - /** - * @param {string} string - * @param {boolean} [template] - * @returns {Tokens} - */ - static tokenize(string, template) { - var tokens = /** @type {Token[]}*/ []; - var source = string; - var position = 0; - var column = 0; - var line = 1; - var lastToken = ""; - var templateBraceCount = 0; - - function inTemplate() { - return template && templateBraceCount === 0; - } - - while (position < source.length) { - if ((currentChar() === "-" && nextChar() === "-" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "-")) - || (currentChar() === "/" && nextChar() === "/" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "/"))) { - consumeComment(); - } else if (currentChar() === "/" && nextChar() === "*" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "*")) { - consumeCommentMultiline(); - } else { - if (Lexer.isWhitespace(currentChar())) { - tokens.push(consumeWhitespace()); - } else if ( - !possiblePrecedingSymbol() && - currentChar() === "." && - (Lexer.isAlpha(nextChar()) || nextChar() === "{" || nextChar() === "-") - ) { - tokens.push(consumeClassReference()); - } else if ( - !possiblePrecedingSymbol() && - currentChar() === "#" && - (Lexer.isAlpha(nextChar()) || nextChar() === "{") - ) { - tokens.push(consumeIdReference()); - } else if (currentChar() === "[" && nextChar() === "@") { - tokens.push(consumeAttributeReference()); - } else if (currentChar() === "@") { - tokens.push(consumeShortAttributeReference()); - } else if (currentChar() === "*" && Lexer.isAlpha(nextChar())) { - tokens.push(consumeStyleReference()); - } else if (Lexer.isAlpha(currentChar()) || (!inTemplate() && Lexer.isIdentifierChar(currentChar()))) { - tokens.push(consumeIdentifier()); - } else if (Lexer.isNumeric(currentChar())) { - tokens.push(consumeNumber()); - } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) { - tokens.push(consumeString()); - } else if (!inTemplate() && currentChar() === "'") { - if (Lexer.isValidSingleQuoteStringStart(tokens)) { - tokens.push(consumeString()); - } else { - tokens.push(consumeOp()); - } - } else if (Lexer.OP_TABLE[currentChar()]) { - if (lastToken === "$" && currentChar() === "{") { - templateBraceCount++; - } - if (currentChar() === "}") { - templateBraceCount--; - } - tokens.push(consumeOp()); - } else if (inTemplate() || Lexer.isReservedChar(currentChar())) { - tokens.push(makeToken("RESERVED", consumeChar())); - } else { - if (position < source.length) { - throw Error("Unknown token: " + currentChar() + " "); - } - } - } - } - - return new Tokens(tokens, [], source); - - /** - * @param {string} [type] - * @param {string} [value] - * @returns {Token} - */ - function makeOpToken(type, value) { - var token = makeToken(type, value); - token.op = true; - return token; - } - - /** - * @param {string} [type] - * @param {string} [value] - * @returns {Token} - */ - function makeToken(type, value) { - return { - type: type, - value: value || "", - start: position, - end: position + 1, - column: column, - line: line, - }; - } - - function consumeComment() { - while (currentChar() && !Lexer.isNewline(currentChar())) { - consumeChar(); - } - consumeChar(); // Consume newline - } - - function consumeCommentMultiline() { - while (currentChar() && !(currentChar() === '*' && nextChar() === '/')) { - consumeChar(); - } - consumeChar(); // Consume "*/" - consumeChar(); - } - - /** - * @returns Token - */ - function consumeClassReference() { - var classRef = makeToken("CLASS_REF"); - var value = consumeChar(); - if (currentChar() === "{") { - classRef.template = true; - value += consumeChar(); - while (currentChar() && currentChar() !== "}") { - value += consumeChar(); - } - if (currentChar() !== "}") { - throw Error("Unterminated class reference"); - } else { - value += consumeChar(); // consume final curly - } - } else { - while (Lexer.isValidCSSClassChar(currentChar())) { - value += consumeChar(); - } - } - classRef.value = value; - classRef.end = position; - return classRef; - } - - /** - * @returns Token - */ - function consumeAttributeReference() { - var attributeRef = makeToken("ATTRIBUTE_REF"); - var value = consumeChar(); - while (position < source.length && currentChar() !== "]") { - value += consumeChar(); - } - if (currentChar() === "]") { - value += consumeChar(); - } - attributeRef.value = value; - attributeRef.end = position; - return attributeRef; - } - - function consumeShortAttributeReference() { - var attributeRef = makeToken("ATTRIBUTE_REF"); - var value = consumeChar(); - while (Lexer.isValidCSSIDChar(currentChar())) { - value += consumeChar(); - } - if (currentChar() === '=') { - value += consumeChar(); - if (currentChar() === '"' || currentChar() === "'") { - let stringValue = consumeString(); - value += stringValue.value; - } else if(Lexer.isAlpha(currentChar()) || - Lexer.isNumeric(currentChar()) || - Lexer.isIdentifierChar(currentChar())) { - let id = consumeIdentifier(); - value += id.value; - } - } - attributeRef.value = value; - attributeRef.end = position; - return attributeRef; - } - - function consumeStyleReference() { - var styleRef = makeToken("STYLE_REF"); - var value = consumeChar(); - while (Lexer.isAlpha(currentChar()) || currentChar() === "-") { - value += consumeChar(); - } - styleRef.value = value; - styleRef.end = position; - return styleRef; - } - - /** - * @returns Token - */ - function consumeIdReference() { - var idRef = makeToken("ID_REF"); - var value = consumeChar(); - if (currentChar() === "{") { - idRef.template = true; - value += consumeChar(); - while (currentChar() && currentChar() !== "}") { - value += consumeChar(); - } - if (currentChar() !== "}") { - throw Error("Unterminated id reference"); - } else { - consumeChar(); // consume final quote - } - } else { - while (Lexer.isValidCSSIDChar(currentChar())) { - value += consumeChar(); - } - } - idRef.value = value; - idRef.end = position; - return idRef; - } - - /** - * @returns Token - */ - function consumeIdentifier() { - var identifier = makeToken("IDENTIFIER"); - var value = consumeChar(); - while (Lexer.isAlpha(currentChar()) || - Lexer.isNumeric(currentChar()) || - Lexer.isIdentifierChar(currentChar())) { - value += consumeChar(); - } - if (currentChar() === "!" && value === "beep") { - value += consumeChar(); - } - identifier.value = value; - identifier.end = position; - return identifier; - } - - /** - * @returns Token - */ - function consumeNumber() { - var number = makeToken("NUMBER"); - var value = consumeChar(); - - // given possible XXX.YYY(e|E)[-]ZZZ consume XXX - while (Lexer.isNumeric(currentChar())) { - value += consumeChar(); - } - - // consume .YYY - if (currentChar() === "." && Lexer.isNumeric(nextChar())) { - value += consumeChar(); - } - while (Lexer.isNumeric(currentChar())) { - value += consumeChar(); - } - - // consume (e|E)[-] - if (currentChar() === "e" || currentChar() === "E") { - // possible scientific notation, e.g. 1e6 or 1e-6 - if (Lexer.isNumeric(nextChar())) { - // e.g. 1e6 - value += consumeChar(); - } else if (nextChar() === "-") { - // e.g. 1e-6 - value += consumeChar(); - // consume the - as well since otherwise we would stop on the next loop - value += consumeChar(); - } - } - - // consume ZZZ - while (Lexer.isNumeric(currentChar())) { - value += consumeChar(); - } - number.value = value; - number.end = position; - return number; - } - - /** - * @returns Token - */ - function consumeOp() { - var op = makeOpToken(); - var value = consumeChar(); // consume leading char - while (currentChar() && Lexer.OP_TABLE[value + currentChar()]) { - value += consumeChar(); - } - op.type = Lexer.OP_TABLE[value]; - op.value = value; - op.end = position; - return op; - } - - /** - * @returns Token - */ - function consumeString() { - var string = makeToken("STRING"); - var startChar = consumeChar(); // consume leading quote - var value = ""; - while (currentChar() && currentChar() !== startChar) { - if (currentChar() === "\\") { - consumeChar(); // consume escape char and get the next one - let nextChar = consumeChar(); - if (nextChar === "b") { - value += "\b"; - } else if (nextChar === "f") { - value += "\f"; - } else if (nextChar === "n") { - value += "\n"; - } else if (nextChar === "r") { - value += "\r"; - } else if (nextChar === "t") { - value += "\t"; - } else if (nextChar === "v") { - value += "\v"; - } else { - value += nextChar; - } - } else { - value += consumeChar(); - } - } - if (currentChar() !== startChar) { - throw Error("Unterminated string at " + Lexer.positionString(string)); - } else { - consumeChar(); // consume final quote - } - string.value = value; - string.end = position; - string.template = startChar === "`"; - return string; - } - - /** - * @returns string - */ - function currentChar() { - return source.charAt(position); - } - - /** - * @returns string - */ - function nextChar() { - return source.charAt(position + 1); - } - - function nextCharAt(number = 1) { - return source.charAt(position + number); - } - - /** - * @returns string - */ - function consumeChar() { - lastToken = currentChar(); - position++; - column++; - return lastToken; - } - - /** - * @returns boolean - */ - function possiblePrecedingSymbol() { - return ( - Lexer.isAlpha(lastToken) || - Lexer.isNumeric(lastToken) || - lastToken === ")" || - lastToken === "\"" || - lastToken === "'" || - lastToken === "`" || - lastToken === "}" || - lastToken === "]" - ); - } - - /** - * @returns Token - */ - function consumeWhitespace() { - var whitespace = makeToken("WHITESPACE"); - var value = ""; - while (currentChar() && Lexer.isWhitespace(currentChar())) { - if (Lexer.isNewline(currentChar())) { - column = 0; - line++; - } - value += consumeChar(); - } - whitespace.value = value; - whitespace.end = position; - return whitespace; - } - } - - /** - * @param {string} string - * @param {boolean} [template] - * @returns {Tokens} - */ - tokenize(string, template) { - return Lexer.tokenize(string, template) - } - } - - /** - * @typedef {Object} Token - * @property {string} [type] - * @property {string} value - * @property {number} [start] - * @property {number} [end] - * @property {number} [column] - * @property {number} [line] - * @property {boolean} [op] `true` if this token represents an operator - * @property {boolean} [template] `true` if this token is a template, for class refs, id refs, strings - */ - - class Tokens { - constructor(tokens, consumed, source) { - this.tokens = tokens - this.consumed = consumed - this.source = source - - this.consumeWhitespace(); // consume initial whitespace - } - - get list() { - return this.tokens - } - - /** @type Token | null */ - _lastConsumed = null; - - consumeWhitespace() { - while (this.token(0, true).type === "WHITESPACE") { - this.consumed.push(this.tokens.shift()); - } - } - - /** - * @param {Tokens} tokens - * @param {*} error - * @returns {never} - */ - raiseError(tokens, error) { - Parser.raiseParseError(tokens, error); - } - - /** - * @param {string} value - * @returns {Token} - */ - requireOpToken(value) { - var token = this.matchOpToken(value); - if (token) { - return token; - } else { - this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'"); - } - } - - /** - * @param {string} op1 - * @param {string} [op2] - * @param {string} [op3] - * @returns {Token | void} - */ - matchAnyOpToken(op1, op2, op3) { - for (var i = 0; i < arguments.length; i++) { - var opToken = arguments[i]; - var match = this.matchOpToken(opToken); - if (match) { - return match; - } - } - } - - /** - * @param {string} op1 - * @param {string} [op2] - * @param {string} [op3] - * @returns {Token | void} - */ - matchAnyToken(op1, op2, op3) { - for (var i = 0; i < arguments.length; i++) { - var opToken = arguments[i]; - var match = this.matchToken(opToken); - if (match) { - return match; - } - } - } - - /** - * @param {string} value - * @returns {Token | void} - */ - matchOpToken(value) { - if (this.currentToken() && this.currentToken().op && this.currentToken().value === value) { - return this.consumeToken(); - } - } - - /** - * @param {string} type1 - * @param {string} [type2] - * @param {string} [type3] - * @param {string} [type4] - * @returns {Token} - */ - requireTokenType(type1, type2, type3, type4) { - var token = this.matchTokenType(type1, type2, type3, type4); - if (token) { - return token; - } else { - this.raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3])); - } - } - - /** - * @param {string} type1 - * @param {string} [type2] - * @param {string} [type3] - * @param {string} [type4] - * @returns {Token | void} - */ - matchTokenType(type1, type2, type3, type4) { - if ( - this.currentToken() && - this.currentToken().type && - [type1, type2, type3, type4].indexOf(this.currentToken().type) >= 0 - ) { - return this.consumeToken(); - } - } - - /** - * @param {string} value - * @param {string} [type] - * @returns {Token} - */ - requireToken(value, type) { - var token = this.matchToken(value, type); - if (token) { - return token; - } else { - this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'"); - } - } - - peekToken(value, peek, type) { - peek = peek || 0; - type = type || "IDENTIFIER"; - return this.tokens[peek] && this.tokens[peek].value === value && this.tokens[peek].type === type - } - - /** - * @param {string} value - * @param {string} [type] - * @returns {Token | void} - */ - matchToken(value, type) { - if (this.follows.indexOf(value) !== -1) { - return; // disallowed token here - } - type = type || "IDENTIFIER"; - if (this.currentToken() && this.currentToken().value === value && this.currentToken().type === type) { - return this.consumeToken(); - } - } - - /** - * @returns {Token} - */ - consumeToken() { - var match = this.tokens.shift(); - this.consumed.push(match); - this._lastConsumed = match; - this.consumeWhitespace(); // consume any whitespace - return match; - } - - /** - * @param {string | null} value - * @param {string | null} [type] - * @returns {Token[]} - */ - consumeUntil(value, type) { - /** @type Token[] */ - var tokenList = []; - var currentToken = this.token(0, true); - - while ( - (type == null || currentToken.type !== type) && - (value == null || currentToken.value !== value) && - currentToken.type !== "EOF" - ) { - var match = this.tokens.shift(); - this.consumed.push(match); - tokenList.push(currentToken); - currentToken = this.token(0, true); - } - this.consumeWhitespace(); // consume any whitespace - return tokenList; - } - - /** - * @returns {string} - */ - lastWhitespace() { - if (this.consumed[this.consumed.length - 1] && this.consumed[this.consumed.length - 1].type === "WHITESPACE") { - return this.consumed[this.consumed.length - 1].value; - } else { - return ""; - } - } - - consumeUntilWhitespace() { - return this.consumeUntil(null, "WHITESPACE"); - } - - /** - * @returns {boolean} - */ - hasMore() { - return this.tokens.length > 0; - } - - /** - * @param {number} n - * @param {boolean} [dontIgnoreWhitespace] - * @returns {Token} - */ - token(n, dontIgnoreWhitespace) { - var /**@type {Token}*/ token; - var i = 0; - do { - if (!dontIgnoreWhitespace) { - while (this.tokens[i] && this.tokens[i].type === "WHITESPACE") { - i++; - } - } - token = this.tokens[i]; - n--; - i++; - } while (n > -1); - if (token) { - return token; - } else { - return { - type: "EOF", - value: "<<>>", - }; - } - } - - /** - * @returns {Token} - */ - currentToken() { - return this.token(0); - } - - /** - * @returns {Token | null} - */ - lastMatch() { - return this._lastConsumed; - } - - /** - * @returns {string} - */ - static sourceFor = function () { - return this.programSource.substring(this.startToken.start, this.endToken.end); - } - - /** - * @returns {string} - */ - static lineFor = function () { - return this.programSource.split("\n")[this.startToken.line - 1]; - } - - follows = []; - - pushFollow(str) { - this.follows.push(str); - } - - popFollow() { - this.follows.pop(); - } - - clearFollows() { - var tmp = this.follows; - this.follows = []; - return tmp; - } - - restoreFollows(f) { - this.follows = f; - } - } - - /** - * @callback ParseRule - * @param {Parser} parser - * @param {Runtime} runtime - * @param {Tokens} tokens - * @param {*} [root] - * @returns {ASTNode | undefined} - * - * @typedef {Object} ASTNode - * @member {boolean} isFeature - * @member {string} type - * @member {any[]} args - * @member {(this: ASTNode, ctx:Context, root:any, ...args:any) => any} op - * @member {(this: ASTNode, context?:Context) => any} evaluate - * @member {ASTNode} parent - * @member {Set} children - * @member {ASTNode} root - * @member {String} keyword - * @member {Token} endToken - * @member {ASTNode} next - * @member {(context:Context) => ASTNode} resolveNext - * @member {EventSource} eventSource - * @member {(this: ASTNode) => void} install - * @member {(this: ASTNode, context:Context) => void} execute - * @member {(this: ASTNode, target: object, source: object, args?: Object) => void} apply - * - * - */ - - class Parser { - /** - * - * @param {Runtime} runtime - */ - constructor(runtime) { - this.runtime = runtime - - this.possessivesDisabled = false - - /* ============================================================================================ */ - /* Core hyperscript Grammar Elements */ - /* ============================================================================================ */ - this.addGrammarElement("feature", function (parser, runtime, tokens) { - if (tokens.matchOpToken("(")) { - var featureElement = parser.requireElement("feature", tokens); - tokens.requireOpToken(")"); - return featureElement; - } - - var featureDefinition = parser.FEATURES[tokens.currentToken().value || ""]; - if (featureDefinition) { - return featureDefinition(parser, runtime, tokens); - } - }); - - this.addGrammarElement("command", function (parser, runtime, tokens) { - if (tokens.matchOpToken("(")) { - const commandElement = parser.requireElement("command", tokens); - tokens.requireOpToken(")"); - return commandElement; - } - - var commandDefinition = parser.COMMANDS[tokens.currentToken().value || ""]; - let commandElement; - if (commandDefinition) { - commandElement = commandDefinition(parser, runtime, tokens); - } else if (tokens.currentToken().type === "IDENTIFIER") { - commandElement = parser.parseElement("pseudoCommand", tokens); - } - if (commandElement) { - return parser.parseElement("indirectStatement", tokens, commandElement); - } - - return commandElement; - }); - - this.addGrammarElement("commandList", function (parser, runtime, tokens) { - if (tokens.hasMore()) { - var cmd = parser.parseElement("command", tokens); - if (cmd) { - tokens.matchToken("then"); - const next = parser.parseElement("commandList", tokens); - if (next) cmd.next = next; - return cmd; - } - } - return { - type: "emptyCommandListCommand", - op: function(context){ - return runtime.findNext(this, context); - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - } - } - }); - - this.addGrammarElement("leaf", function (parser, runtime, tokens) { - var result = parser.parseAnyOf(parser.LEAF_EXPRESSIONS, tokens); - // symbol is last so it doesn't consume any constants - if (result == null) { - return parser.parseElement("symbol", tokens); - } - - return result; - }); - - this.addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) { - for (var i = 0; i < parser.INDIRECT_EXPRESSIONS.length; i++) { - var indirect = parser.INDIRECT_EXPRESSIONS[i]; - root.endToken = tokens.lastMatch(); - var result = parser.parseElement(indirect, tokens, root); - if (result) { - return result; - } - } - return root; - }); - - this.addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) { - if (tokens.matchToken("unless")) { - root.endToken = tokens.lastMatch(); - var conditional = parser.requireElement("expression", tokens); - var unless = { - type: "unlessStatementModifier", - args: [conditional], - op: function (context, conditional) { - if (conditional) { - return this.next; - } else { - return root; - } - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - }; - root.parent = unless; - return unless; - } - return root; - }); - - this.addGrammarElement("primaryExpression", function (parser, runtime, tokens) { - var leaf = parser.parseElement("leaf", tokens); - if (leaf) { - return parser.parseElement("indirectExpression", tokens, leaf); - } - parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value); - }); - } - - use(plugin) { - plugin(this) - return this - } - - /** @type {Object} */ - GRAMMAR = {}; - - /** @type {Object} */ - COMMANDS = {}; - - /** @type {Object} */ - FEATURES = {}; - - /** @type {string[]} */ - LEAF_EXPRESSIONS = []; - /** @type {string[]} */ - INDIRECT_EXPRESSIONS = []; - - /** - * @param {*} parseElement - * @param {*} start - * @param {Tokens} tokens - */ - initElt(parseElement, start, tokens) { - parseElement.startToken = start; - parseElement.sourceFor = Tokens.sourceFor; - parseElement.lineFor = Tokens.lineFor; - parseElement.programSource = tokens.source; - } - - /** - * @param {string} type - * @param {Tokens} tokens - * @param {ASTNode?} root - * @returns {ASTNode} - */ - parseElement(type, tokens, root = undefined) { - var elementDefinition = this.GRAMMAR[type]; - if (elementDefinition) { - var start = tokens.currentToken(); - var parseElement = elementDefinition(this, this.runtime, tokens, root); - if (parseElement) { - this.initElt(parseElement, start, tokens); - parseElement.endToken = parseElement.endToken || tokens.lastMatch(); - var root = parseElement.root; - while (root != null) { - this.initElt(root, start, tokens); - root = root.root; - } - } - return parseElement; - } - } - - /** - * @param {string} type - * @param {Tokens} tokens - * @param {string} [message] - * @param {*} [root] - * @returns {ASTNode} - */ - requireElement(type, tokens, message, root) { - var result = this.parseElement(type, tokens, root); - if (!result) Parser.raiseParseError(tokens, message || "Expected " + type); - // @ts-ignore - return result; - } - - /** - * @param {string[]} types - * @param {Tokens} tokens - * @returns {ASTNode} - */ - parseAnyOf(types, tokens) { - for (var i = 0; i < types.length; i++) { - var type = types[i]; - var expression = this.parseElement(type, tokens); - if (expression) { - return expression; - } - } - } - - /** - * @param {string} name - * @param {ParseRule} definition - */ - addGrammarElement(name, definition) { - this.GRAMMAR[name] = definition; - } - - /** - * @param {string} keyword - * @param {ParseRule} definition - */ - addCommand(keyword, definition) { - var commandGrammarType = keyword + "Command"; - var commandDefinitionWrapper = function (parser, runtime, tokens) { - const commandElement = definition(parser, runtime, tokens); - if (commandElement) { - commandElement.type = commandGrammarType; - commandElement.execute = function (context) { - context.meta.command = commandElement; - return runtime.unifiedExec(this, context); - }; - return commandElement; - } - }; - this.GRAMMAR[commandGrammarType] = commandDefinitionWrapper; - this.COMMANDS[keyword] = commandDefinitionWrapper; - } - - /** - * @param {string} keyword - * @param {ParseRule} definition - */ - addFeature(keyword, definition) { - var featureGrammarType = keyword + "Feature"; - - /** @type {ParseRule} */ - var featureDefinitionWrapper = function (parser, runtime, tokens) { - var featureElement = definition(parser, runtime, tokens); - if (featureElement) { - featureElement.isFeature = true; - featureElement.keyword = keyword; - featureElement.type = featureGrammarType; - return featureElement; - } - }; - this.GRAMMAR[featureGrammarType] = featureDefinitionWrapper; - this.FEATURES[keyword] = featureDefinitionWrapper; - } - - /** - * @param {string} name - * @param {ParseRule} definition - */ - addLeafExpression(name, definition) { - this.LEAF_EXPRESSIONS.push(name); - this.addGrammarElement(name, definition); - } - - /** - * @param {string} name - * @param {ParseRule} definition - */ - addIndirectExpression(name, definition) { - this.INDIRECT_EXPRESSIONS.push(name); - this.addGrammarElement(name, definition); - } - - /** - * - * @param {Tokens} tokens - * @returns string - */ - static createParserContext(tokens) { - var currentToken = tokens.currentToken(); - var source = tokens.source; - var lines = source.split("\n"); - var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1; - var contextLine = lines[line]; - var offset = /** @type {number} */ ( - currentToken && currentToken.line ? currentToken.column : contextLine.length - 1); - return contextLine + "\n" + " ".repeat(offset) + "^^\n\n"; - } - - /** - * @param {Tokens} tokens - * @param {string} [message] - * @returns {never} - */ - static raiseParseError(tokens, message) { - message = - (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + Parser.createParserContext(tokens); - var error = new Error(message); - error["tokens"] = tokens; - throw error; - } - - /** - * @param {Tokens} tokens - * @param {string} [message] - */ - raiseParseError(tokens, message) { - Parser.raiseParseError(tokens, message) - } - - /** - * @param {Tokens} tokens - * @returns {ASTNode} - */ - parseHyperScript(tokens) { - var result = this.parseElement("hyperscript", tokens); - if (tokens.hasMore()) this.raiseParseError(tokens); - if (result) return result; - } - - /** - * @param {ASTNode | undefined} elt - * @param {ASTNode} parent - */ - setParent(elt, parent) { - if (typeof elt === 'object') { - elt.parent = parent; - if (typeof parent === 'object') { - parent.children = (parent.children || new Set()); - parent.children.add(elt) - } - this.setParent(elt.next, parent); - } - } - - /** - * @param {Token} token - * @returns {ParseRule} - */ - commandStart(token) { - return this.COMMANDS[token.value || ""]; - } - - /** - * @param {Token} token - * @returns {ParseRule} - */ - featureStart(token) { - return this.FEATURES[token.value || ""]; - } - - /** - * @param {Token} token - * @returns {boolean} - */ - commandBoundary(token) { - if ( - token.value == "end" || - token.value == "then" || - token.value == "else" || - token.value == "otherwise" || - token.value == ")" || - this.commandStart(token) || - this.featureStart(token) || - token.type == "EOF" - ) { - return true; - } - return false; - } - - /** - * @param {Tokens} tokens - * @returns {(string | ASTNode)[]} - */ - parseStringTemplate(tokens) { - /** @type {(string | ASTNode)[]} */ - var returnArr = [""]; - do { - returnArr.push(tokens.lastWhitespace()); - if (tokens.currentToken().value === "$") { - tokens.consumeToken(); - var startingBrace = tokens.matchOpToken("{"); - returnArr.push(this.requireElement("expression", tokens)); - if (startingBrace) { - tokens.requireOpToken("}"); - } - returnArr.push(""); - } else if (tokens.currentToken().value === "\\") { - tokens.consumeToken(); // skip next - tokens.consumeToken(); - } else { - var token = tokens.consumeToken(); - returnArr[returnArr.length - 1] += token ? token.value : ""; - } - } while (tokens.hasMore()); - returnArr.push(tokens.lastWhitespace()); - return returnArr; - } - - /** - * @param {ASTNode} commandList - */ - ensureTerminated(commandList) { - const runtime = this.runtime - var implicitReturn = { - type: "implicitReturn", - op: function (context) { - context.meta.returned = true; - if (context.meta.resolve) { - context.meta.resolve(); - } - return runtime.HALT; - }, - execute: function (ctx) { - // do nothing - }, - }; - - var end = commandList; - while (end.next) { - end = end.next; - } - end.next = implicitReturn; - } - } - - class Runtime { - /** - * - * @param {Lexer} [lexer] - * @param {Parser} [parser] - */ - constructor(lexer, parser) { - this.lexer = lexer ?? new Lexer; - this.parser = parser ?? new Parser(this) - .use(hyperscriptCoreGrammar) - .use(hyperscriptWebGrammar); - this.parser.runtime = this - } - - /** - * @param {HTMLElement} elt - * @param {string} selector - * @returns boolean - */ - matchesSelector(elt, selector) { - // noinspection JSUnresolvedVariable - var matchesFunction = - // @ts-ignore - elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; - return matchesFunction && matchesFunction.call(elt, selector); - } - - /** - * @param {string} eventName - * @param {Object} [detail] - * @returns {Event} - */ - makeEvent(eventName, detail) { - var evt; - if (globalScope.Event && typeof globalScope.Event === "function") { - evt = new Event(eventName, { - bubbles: true, - cancelable: true, - }); - evt['detail'] = detail; - } else { - evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(eventName, true, true, detail); - } - return evt; - } - - /** - * @param {Element} elt - * @param {string} eventName - * @param {Object} [detail] - * @param {Element} [sender] - * @returns {boolean} - */ - triggerEvent(elt, eventName, detail, sender) { - detail = detail || {}; - detail["sender"] = sender; - var event = this.makeEvent(eventName, detail); - var eventResult = elt.dispatchEvent(event); - return eventResult; - } - - /** - * isArrayLike returns `true` if the provided value is an array or - * a NodeList (which is close enough to being an array for our purposes). - * - * @param {any} value - * @returns {value is Array | NodeList} - */ - isArrayLike(value) { - return Array.isArray(value) || - (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection)); - } - - /** - * isIterable returns `true` if the provided value supports the - * iterator protocol. - * - * @param {any} value - * @returns {value is Iterable} - */ - isIterable(value) { - return typeof value === 'object' - && Symbol.iterator in value - && typeof value[Symbol.iterator] === 'function'; - } - - /** - * shouldAutoIterate returns `true` if the provided value - * should be implicitly iterated over when accessing properties, - * and as the target of some commands. - * - * Currently, this is when the value is an {ElementCollection} - * or {isArrayLike} returns true. - * - * @param {any} value - * @returns {value is (any[] | ElementCollection)} - */ - shouldAutoIterate(value) { - return value != null && value[shouldAutoIterateSymbol] || - this.isArrayLike(value); - } - - /** - * forEach executes the provided `func` on every item in the `value` array. - * if `value` is a single item (and not an array) then `func` is simply called - * once. If `value` is null, then no further actions are taken. - * - * @template T - * @param {T | Iterable} value - * @param {(item: T) => void} func - */ - forEach(value, func) { - if (value == null) { - // do nothing - } else if (this.isIterable(value)) { - for (const nth of value) { - func(nth); - } - } else if (this.isArrayLike(value)) { - for (var i = 0; i < value.length; i++) { - func(value[i]); - } - } else { - func(value); - } - } - - /** - * implicitLoop executes the provided `func` on: - * - every item of {value}, if {value} should be auto-iterated - * (see {shouldAutoIterate}) - * - {value} otherwise - * - * @template T - * @param {ElementCollection | T | T[]} value - * @param {(item: T) => void} func - */ - implicitLoop(value, func) { - if (this.shouldAutoIterate(value)) { - for (const x of value) func(x); - } else { - func(value); - } - } - - wrapArrays(args) { - var arr = []; - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - if (Array.isArray(arg)) { - arr.push(Promise.all(arg)); - } else { - arr.push(arg); - } - } - return arr; - } - - unwrapAsyncs(values) { - for (var i = 0; i < values.length; i++) { - var value = values[i]; - if (value.asyncWrapper) { - values[i] = value.value; - } - if (Array.isArray(value)) { - for (var j = 0; j < value.length; j++) { - var valueElement = value[j]; - if (valueElement.asyncWrapper) { - value[j] = valueElement.value; - } - } - } - } - } - - static HALT = {}; - HALT = Runtime.HALT; - - /** - * @param {ASTNode} command - * @param {Context} ctx - */ - unifiedExec(command, ctx) { - while (true) { - try { - var next = this.unifiedEval(command, ctx); - } catch (e) { - if (ctx.meta.handlingFinally) { - console.error(" Exception in finally block: ", e); - next = Runtime.HALT; - } else { - this.registerHyperTrace(ctx, e); - if (ctx.meta.errorHandler && !ctx.meta.handlingError) { - ctx.meta.handlingError = true; - ctx.locals[ctx.meta.errorSymbol] = e; - command = ctx.meta.errorHandler; - continue; - } else { - ctx.meta.currentException = e; - next = Runtime.HALT; - } - } - } - if (next == null) { - console.error(command, " did not return a next element to execute! context: ", ctx); - return; - } else if (next.then) { - next.then(resolvedNext => { - this.unifiedExec(resolvedNext, ctx); - }).catch(reason => { - this.unifiedExec({ // Anonymous command to simply throw the exception - op: function(){ - throw reason; - } - }, ctx); - }); - return; - } else if (next === Runtime.HALT) { - if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) { - ctx.meta.handlingFinally = true; - command = ctx.meta.finallyHandler; - } else { - if (ctx.meta.onHalt) { - ctx.meta.onHalt(); - } - if (ctx.meta.currentException) { - if (ctx.meta.reject) { - ctx.meta.reject(ctx.meta.currentException); - return; - } else { - throw ctx.meta.currentException; - } - } else { - return; - } - } - } else { - command = next; // move to the next command - } - } - } - - /** - * @param {*} parseElement - * @param {Context} ctx - * @returns {*} - */ - unifiedEval(parseElement, ctx) { - /** @type any[] */ - var args = [ctx]; - var async = false; - var wrappedAsyncs = false; - - if (parseElement.args) { - for (var i = 0; i < parseElement.args.length; i++) { - var argument = parseElement.args[i]; - if (argument == null) { - args.push(null); - } else if (Array.isArray(argument)) { - var arr = []; - for (var j = 0; j < argument.length; j++) { - var element = argument[j]; - var value = element ? element.evaluate(ctx) : null; // OK - if (value) { - if (value.then) { - async = true; - } else if (value.asyncWrapper) { - wrappedAsyncs = true; - } - } - arr.push(value); - } - args.push(arr); - } else if (argument.evaluate) { - var value = argument.evaluate(ctx); // OK - if (value) { - if (value.then) { - async = true; - } else if (value.asyncWrapper) { - wrappedAsyncs = true; - } - } - args.push(value); - } else { - args.push(argument); - } - } - } - if (async) { - return new Promise((resolve, reject) => { - args = this.wrapArrays(args); - Promise.all(args) - .then(function (values) { - if (wrappedAsyncs) { - this.unwrapAsyncs(values); - } - try { - var apply = parseElement.op.apply(parseElement, values); - resolve(apply); - } catch (e) { - reject(e); - } - }) - .catch(function (reason) { - reject(reason); - }); - }); - } else { - if (wrappedAsyncs) { - this.unwrapAsyncs(args); - } - return parseElement.op.apply(parseElement, args); - } - } - - /** - * @type {string[] | null} - */ - _scriptAttrs = null; - - /** - * getAttributes returns the attribute name(s) to use when - * locating hyperscript scripts in a DOM element. If no value - * has been configured, it defaults to config.attributes - * @returns string[] - */ - getScriptAttributes() { - if (this._scriptAttrs == null) { - this._scriptAttrs = config.attributes.replace(/ /g, "").split(","); - } - return this._scriptAttrs; - } - - /** - * @param {Element} elt - * @returns {string | null} - */ - getScript(elt) { - for (var i = 0; i < this.getScriptAttributes().length; i++) { - var scriptAttribute = this.getScriptAttributes()[i]; - if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) { - return elt.getAttribute(scriptAttribute); - } - } - if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") { - return elt.innerText; - } - return null; - } - - hyperscriptFeaturesMap = new WeakMap - - /** - * @param {*} elt - * @returns {Object} - */ - getHyperscriptFeatures(elt) { - var hyperscriptFeatures = this.hyperscriptFeaturesMap.get(elt); - if (typeof hyperscriptFeatures === 'undefined') { - if (elt) { - // in some rare cases, elt is null and this line crashes - this.hyperscriptFeaturesMap.set(elt, hyperscriptFeatures = {}); - } - } - return hyperscriptFeatures; - } - - /** - * @param {Object} owner - * @param {Context} ctx - */ - addFeatures(owner, ctx) { - if (owner) { - Object.assign(ctx.locals, this.getHyperscriptFeatures(owner)); - this.addFeatures(owner.parentElement, ctx); - } - } - - /** - * @param {*} owner - * @param {*} feature - * @param {*} hyperscriptTarget - * @param {*} event - * @returns {Context} - */ - makeContext(owner, feature, hyperscriptTarget, event) { - return new Context(owner, feature, hyperscriptTarget, event, this) - } - - /** - * @returns string - */ - getScriptSelector() { - return this.getScriptAttributes() - .map(function (attribute) { - return "[" + attribute + "]"; - }) - .join(", "); - } - - /** - * @param {any} value - * @param {string} type - * @returns {any} - */ - convertValue(value, type) { - var dynamicResolvers = conversions.dynamicResolvers; - for (var i = 0; i < dynamicResolvers.length; i++) { - var dynamicResolver = dynamicResolvers[i]; - var converted = dynamicResolver(type, value); - if (converted !== undefined) { - return converted; - } - } - - if (value == null) { - return null; - } - var converter = conversions[type]; - if (converter) { - return converter(value); - } - - throw "Unknown conversion : " + type; - } - - /** - * @param {string} src - * @returns {ASTNode} - */ - parse(src) { - const lexer = this.lexer, parser = this.parser - var tokens = lexer.tokenize(src); - if (this.parser.commandStart(tokens.currentToken())) { - var commandList = parser.requireElement("commandList", tokens); - if (tokens.hasMore()) parser.raiseParseError(tokens); - parser.ensureTerminated(commandList); - return commandList; - } else if (parser.featureStart(tokens.currentToken())) { - var hyperscript = parser.requireElement("hyperscript", tokens); - if (tokens.hasMore()) parser.raiseParseError(tokens); - return hyperscript; - } else { - var expression = parser.requireElement("expression", tokens); - if (tokens.hasMore()) parser.raiseParseError(tokens); - return expression; - } - } - - /** - * - * @param {ASTNode} elt - * @param {Context} ctx - * @returns {any} - */ - evaluateNoPromise(elt, ctx) { - let result = elt.evaluate(ctx); - if (result.next) { - throw new Error(Tokens.sourceFor.call(elt) + " returned a Promise in a context that they are not allowed."); - } - return result; - } - - /** - * @param {string} src - * @param {Partial} [ctx] - * @param {Object} [args] - * @returns {any} - */ - evaluate(src, ctx, args) { - class HyperscriptModule extends EventTarget { - constructor(mod) { - super(); - this.module = mod; - } - toString() { - return this.module.id; - } - } - - var body = 'document' in globalScope - ? globalScope.document.body - : new HyperscriptModule(args && args.module); - ctx = Object.assign(this.makeContext(body, null, body, null), ctx || {}); - var element = this.parse(src); - if (element.execute) { - element.execute(ctx); - return ctx.result; - } else if (element.apply) { - element.apply(body, body, args); - return this.getHyperscriptFeatures(body); - } else { - return element.evaluate(ctx); - } - - function makeModule() { - return {} - } - } - - /** - * @param {HTMLElement} elt - */ - processNode(elt) { - var selector = this.getScriptSelector(); - if (this.matchesSelector(elt, selector)) { - this.initElement(elt, elt); - } - if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") { - this.initElement(elt, document.body); - } - if (elt.querySelectorAll) { - this.forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), elt => { - this.initElement(elt, elt instanceof HTMLScriptElement && elt.type === "text/hyperscript" ? document.body : elt); - }); - } - } - - /** - * @param {Element} elt - * @param {Element} [target] - */ - initElement(elt, target) { - if (elt.closest && elt.closest(config.disableSelector)) { - return; - } - var internalData = this.getInternalData(elt); - if (!internalData.initialized) { - var src = this.getScript(elt); - if (src) { - try { - internalData.initialized = true; - internalData.script = src; - const lexer = this.lexer, parser = this.parser - var tokens = lexer.tokenize(src); - var hyperScript = parser.parseHyperScript(tokens); - if (!hyperScript) return; - hyperScript.apply(target || elt, elt); - setTimeout(() => { - this.triggerEvent(target || elt, "load", { - hyperscript: true, - }); - }, 1); - } catch (e) { - this.triggerEvent(elt, "exception", { - error: e, - }); - console.error( - "hyperscript errors were found on the following element:", - elt, - "\n\n", - e.message, - e.stack - ); - } - } - } - } - - internalDataMap = new WeakMap - - /** - * @param {Element} elt - * @returns {Object} - */ - getInternalData(elt) { - var internalData = this.internalDataMap.get(elt); - if (typeof internalData === 'undefined') { - this.internalDataMap.set(elt, internalData = {}); - } - return internalData; - } - - /** - * @param {any} value - * @param {string} typeString - * @param {boolean} [nullOk] - * @returns {boolean} - */ - typeCheck(value, typeString, nullOk) { - if (value == null && nullOk) { - return true; - } - var typeName = Object.prototype.toString.call(value).slice(8, -1); - return typeName === typeString; - } - - getElementScope(context) { - var elt = context.meta && context.meta.owner; - if (elt) { - var internalData = this.getInternalData(elt); - var scopeName = "elementScope"; - if (context.meta.feature && context.meta.feature.behavior) { - scopeName = context.meta.feature.behavior + "Scope"; - } - var elementScope = getOrInitObject(internalData, scopeName); - return elementScope; - } else { - return {}; // no element, return empty scope - } - } - - /** - * @param {string} str - * @returns {boolean} - */ - isReservedWord(str) { - return ["meta", "it", "result", "locals", "event", "target", "detail", "sender", "body"].includes(str) - } - - /** - * @param {any} context - * @returns {boolean} - */ - isHyperscriptContext(context) { - return context instanceof Context; - } - - /** - * @param {string} str - * @param {Context} context - * @returns {any} - */ - resolveSymbol(str, context, type) { - if (str === "me" || str === "my" || str === "I") { - return context.me; - } - if (str === "it" || str === "its" || str === "result") { - return context.result; - } - if (str === "you" || str === "your" || str === "yourself") { - return context.you; - } else { - if (type === "global") { - return globalScope[str]; - } else if (type === "element") { - var elementScope = this.getElementScope(context); - return elementScope[str]; - } else if (type === "local") { - return context.locals[str]; - } else { - // meta scope (used for event conditionals) - if (context.meta && context.meta.context) { - var fromMetaContext = context.meta.context[str]; - if (typeof fromMetaContext !== "undefined") { - return fromMetaContext; - } - // resolve against the `detail` object in the meta context as well - if (context.meta.context.detail) { - fromMetaContext = context.meta.context.detail[str]; - if (typeof fromMetaContext !== "undefined") { - return fromMetaContext; - } - } - } - if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) { - // local scope - var fromContext = context.locals[str]; - } else { - // direct get from normal JS object or top-level of context - var fromContext = context[str]; - } - if (typeof fromContext !== "undefined") { - return fromContext; - } else { - // element scope - var elementScope = this.getElementScope(context); - fromContext = elementScope[str]; - if (typeof fromContext !== "undefined") { - return fromContext; - } else { - // global scope - return globalScope[str]; - } - } - } - } - } - - setSymbol(str, context, type, value) { - if (type === "global") { - globalScope[str] = value; - } else if (type === "element") { - var elementScope = this.getElementScope(context); - elementScope[str] = value; - } else if (type === "local") { - context.locals[str] = value; - } else { - if (this.isHyperscriptContext(context) && !this.isReservedWord(str) && typeof context.locals[str] !== "undefined") { - // local scope - context.locals[str] = value; - } else { - // element scope - var elementScope = this.getElementScope(context); - var fromContext = elementScope[str]; - if (typeof fromContext !== "undefined") { - elementScope[str] = value; - } else { - if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) { - // local scope - context.locals[str] = value; - } else { - // direct set on normal JS object or top-level of context - context[str] = value; - } - } - } - } - } - - /** - * @param {ASTNode} command - * @param {Context} context - * @returns {undefined | ASTNode} - */ - findNext(command, context) { - if (command) { - if (command.resolveNext) { - return command.resolveNext(context); - } else if (command.next) { - return command.next; - } else { - return this.findNext(command.parent, context); - } - } - } - - /** - * @param {Object} root - * @param {string} property - * @param {Getter} getter - * @returns {any} - * - * @callback Getter - * @param {Object} root - * @param {string} property - */ - flatGet(root, property, getter) { - if (root != null) { - var val = getter(root, property); - if (typeof val !== "undefined") { - return val; - } - - if (this.shouldAutoIterate(root)) { - // flat map - var result = []; - for (var component of root) { - var componentValue = getter(component, property); - result.push(componentValue); - } - return result; - } - } - } - - resolveProperty(root, property) { - return this.flatGet(root, property, (root, property) => root[property] ) - } - - resolveAttribute(root, property) { - return this.flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property) ) - } - - /** - * - * @param {Object} root - * @param {string} property - * @returns {string} - */ - resolveStyle(root, property) { - return this.flatGet(root, property, (root, property) => root.style && root.style[property] ) - } - - /** - * - * @param {Object} root - * @param {string} property - * @returns {string} - */ - resolveComputedStyle(root, property) { - return this.flatGet(root, property, (root, property) => getComputedStyle( - /** @type {Element} */ (root)).getPropertyValue(property) ) - } - - /** - * @param {Element} elt - * @param {string[]} nameSpace - * @param {string} name - * @param {any} value - */ - assignToNamespace(elt, nameSpace, name, value) { - let root - if (typeof document !== "undefined" && elt === document.body) { - root = globalScope; - } else { - root = this.getHyperscriptFeatures(elt); - } - var propertyName; - while ((propertyName = nameSpace.shift()) !== undefined) { - var newRoot = root[propertyName]; - if (newRoot == null) { - newRoot = {}; - root[propertyName] = newRoot; - } - root = newRoot; - } - - root[name] = value; - } - - getHyperTrace(ctx, thrown) { - var trace = []; - var root = ctx; - while (root.meta.caller) { - root = root.meta.caller; - } - if (root.meta.traceMap) { - return root.meta.traceMap.get(thrown, trace); - } - } - - registerHyperTrace(ctx, thrown) { - var trace = []; - var root = null; - while (ctx != null) { - trace.push(ctx); - root = ctx; - ctx = ctx.meta.caller; - } - if (root.meta.traceMap == null) { - root.meta.traceMap = new Map(); // TODO - WeakMap? - } - if (!root.meta.traceMap.get(thrown)) { - var traceEntry = { - trace: trace, - print: function (logger) { - logger = logger || console.error; - logger("hypertrace /// "); - var maxLen = 0; - for (var i = 0; i < trace.length; i++) { - maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length); - } - for (var i = 0; i < trace.length; i++) { - var traceElt = trace[i]; - logger( - " ->", - traceElt.meta.feature.displayName.padEnd(maxLen + 2), - "-", - traceElt.meta.owner - ); - } - }, - }; - root.meta.traceMap.set(thrown, traceEntry); - } - } - - /** - * @param {string} str - * @returns {string} - */ - escapeSelector(str) { - return str.replace(/:/g, function (str) { - return "\\" + str; - }); - } - - /** - * @param {any} value - * @param {*} elt - */ - nullCheck(value, elt) { - if (value == null) { - throw new Error("'" + elt.sourceFor() + "' is null"); - } - } - - /** - * @param {any} value - * @returns {boolean} - */ - isEmpty(value) { - return value == undefined || value.length === 0; - } - - /** - * @param {any} value - * @returns {boolean} - */ - doesExist(value) { - if(value == null){ - return false; - } - if (this.shouldAutoIterate(value)) { - for (const elt of value) { - return true; - } - return false; - } - return true; - } - - /** - * @param {Node} node - * @returns {Document|ShadowRoot} - */ - getRootNode(node) { - if (node && node instanceof Node) { - var rv = node.getRootNode(); - if (rv instanceof Document || rv instanceof ShadowRoot) return rv; - } - return document; - } - - /** - * - * @param {Element} elt - * @param {ASTNode} onFeature - * @returns {EventQueue} - * - * @typedef {{queue:Array, executing:boolean}} EventQueue - */ - getEventQueueFor(elt, onFeature) { - let internalData = this.getInternalData(elt); - var eventQueuesForElt = internalData.eventQueues; - if (eventQueuesForElt == null) { - eventQueuesForElt = new Map(); - internalData.eventQueues = eventQueuesForElt; - } - var eventQueueForFeature = eventQueuesForElt.get(onFeature); - if (eventQueueForFeature == null) { - eventQueueForFeature = {queue:[], executing:false}; - eventQueuesForElt.set(onFeature, eventQueueForFeature); - } - return eventQueueForFeature; - } - - beepValueToConsole(element, expression, value) { - if (this.triggerEvent(element, "hyperscript:beep", {element, expression, value})) { - var typeName; - if (value) { - if (value instanceof ElementCollection) { - typeName = "ElementCollection"; - } else if (value.constructor) { - typeName = value.constructor.name; - } else { - typeName = "unknown"; - } - } else { - typeName = "object (null)" - } - var logValue = value; - if (typeName === "String") { - logValue = '"' + logValue + '"'; - } else if (value instanceof ElementCollection) { - logValue = Array.from(value); - } - console.log("///_ BEEP! The expression (" + Tokens.sourceFor.call(expression).replace("beep! ", "") + ") evaluates to:", logValue, "of type " + typeName); - } - } - - - /** @type string | null */ - // @ts-ignore - hyperscriptUrl = "document" in globalScope && document.currentScript ? document.currentScript.src : null; - } - - - function getCookiesAsArray() { - let cookiesAsArray = document.cookie - .split("; ") - .map(cookieEntry => { - let strings = cookieEntry.split("="); - return {name: strings[0], value: decodeURIComponent(strings[1])} - }); - return cookiesAsArray; - } - - function clearCookie(name) { - document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; - } - - function clearAllCookies() { - for (const cookie of getCookiesAsArray()) { - clearCookie(cookie.name); - } - } - - const CookieJar = new Proxy({}, { - get(target, prop) { - if (prop === 'then' || prop === 'asyncWrapper') { // ignore special symbols - return null; - } else if (prop === 'length') { - return getCookiesAsArray().length - } else if (prop === 'clear') { - return clearCookie; - } else if (prop === 'clearAll') { - return clearAllCookies; - } else if (typeof prop === "string") { - if (!isNaN(prop)) { - return getCookiesAsArray()[parseInt(prop)]; - - } else { - let value = document.cookie - .split("; ") - .find((row) => row.startsWith(prop + "=")) - ?.split("=")[1]; - if(value) { - return decodeURIComponent(value); - } - } - } else if (prop === Symbol.iterator) { - return getCookiesAsArray()[prop]; - } - }, - set(target, prop, value) { - var finalValue = null; - if ('string' === typeof value) { - finalValue = encodeURIComponent(value) - finalValue += ";samesite=lax" - } else { - finalValue = encodeURIComponent(value.value); - if (value.expires) { - finalValue+=";expires=" + value.maxAge; - } - if (value.maxAge) { - finalValue+=";max-age=" + value.maxAge; - } - if (value.partitioned) { - finalValue+=";partitioned=" + value.partitioned; - } - if (value.path) { - finalValue+=";path=" + value.path; - } - if (value.samesite) { - finalValue+=";samesite=" + value.path; - } - if (value.secure) { - finalValue+=";secure=" + value.path; - } - } - document.cookie= prop + "=" + value; - return true; - } - }) - - class Context { - /** - * @param {*} owner - * @param {*} feature - * @param {*} hyperscriptTarget - * @param {*} event - */ - constructor(owner, feature, hyperscriptTarget, event, runtime) { - this.meta = { - parser: runtime.parser, - lexer: runtime.lexer, - runtime, - owner: owner, - feature: feature, - iterators: {}, - ctx: this - } - this.locals = { - cookies:CookieJar - }; - this.me = hyperscriptTarget, - this.you = undefined - this.result = undefined - this.event = event; - this.target = event ? event.target : null; - this.detail = event ? event.detail : null; - this.sender = event ? event.detail ? event.detail.sender : null : null; - this.body = "document" in globalScope ? document.body : null; - runtime.addFeatures(owner, this); - } - } - - class ElementCollection { - constructor(css, relativeToElement, escape) { - this._css = css; - this.relativeToElement = relativeToElement; - this.escape = escape; - this[shouldAutoIterateSymbol] = true; - } - - get css() { - if (this.escape) { - return Runtime.prototype.escapeSelector(this._css); - } else { - return this._css; - } - } - - get className() { - return this._css.substr(1); - } - - get id() { - return this.className(); - } - - contains(elt) { - for (let element of this) { - if (element.contains(elt)) { - return true; - } - } - return false; - } - - get length() { - return this.selectMatches().length; - } - - [Symbol.iterator]() { - let query = this.selectMatches(); - return query [Symbol.iterator](); - } - - selectMatches() { - let query = Runtime.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css); - return query; - } - } - - const shouldAutoIterateSymbol = Symbol() - - function getOrInitObject(root, prop) { - var value = root[prop]; - if (value) { - return value; - } else { - var newObj = {}; - root[prop] = newObj; - return newObj; - } - } - - /** - * parseJSON parses a JSON string into a corresponding value. If the - * value passed in is not valid JSON, then it logs an error and returns `null`. - * - * @param {string} jString - * @returns any - */ - function parseJSON(jString) { - try { - return JSON.parse(jString); - } catch (error) { - logError(error); - return null; - } - } - - /** - * logError writes an error message to the Javascript console. It can take any - * value, but msg should commonly be a simple string. - * @param {*} msg - */ - function logError(msg) { - if (console.error) { - console.error(msg); - } else if (console.log) { - console.log("ERROR: ", msg); - } - } - - // TODO: JSDoc description of what's happening here - function varargConstructor(Cls, args) { - return new (Cls.bind.apply(Cls, [Cls].concat(args)))(); - } - - // Grammar - - /** - * @param {Parser} parser - */ - function hyperscriptCoreGrammar(parser) { - parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) { - if (tokens.matchOpToken("(")) { - var follows = tokens.clearFollows(); - try { - var expr = parser.requireElement("expression", tokens); - } finally { - tokens.restoreFollows(follows); - } - tokens.requireOpToken(")"); - return expr; - } - }); - - parser.addLeafExpression("string", function (parser, runtime, tokens) { - var stringToken = tokens.matchTokenType("STRING"); - if (!stringToken) return; - var rawValue = /** @type {string} */ (stringToken.value); - /** @type {any[]} */ - var args; - if (stringToken.template) { - var innerTokens = Lexer.tokenize(rawValue, true); - args = parser.parseStringTemplate(innerTokens); - } else { - args = []; - } - return { - type: "string", - token: stringToken, - args: args, - op: function (context) { - var returnStr = ""; - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (val !== undefined) { - returnStr += val; - } - } - return returnStr; - }, - evaluate: function (context) { - if (args.length === 0) { - return rawValue; - } else { - return runtime.unifiedEval(this, context); - } - }, - }; - }); - - parser.addGrammarElement("nakedString", function (parser, runtime, tokens) { - if (tokens.hasMore()) { - var tokenArr = tokens.consumeUntilWhitespace(); - tokens.matchTokenType("WHITESPACE"); - return { - type: "nakedString", - tokens: tokenArr, - evaluate: function (context) { - return tokenArr - .map(function (t) { - return t.value; - }) - .join(""); - }, - }; - } - }); - - parser.addLeafExpression("number", function (parser, runtime, tokens) { - var number = tokens.matchTokenType("NUMBER"); - if (!number) return; - var numberToken = number; - var value = parseFloat(/** @type {string} */ (number.value)); - return { - type: "number", - value: value, - numberToken: numberToken, - evaluate: function () { - return value; - }, - }; - }); - - parser.addLeafExpression("idRef", function (parser, runtime, tokens) { - var elementId = tokens.matchTokenType("ID_REF"); - if (!elementId) return; - if (!elementId.value) return; - // TODO - unify these two expression types - if (elementId.template) { - var templateValue = elementId.value.substring(2); - var innerTokens = Lexer.tokenize(templateValue); - var innerExpression = parser.requireElement("expression", innerTokens); - return { - type: "idRefTemplate", - args: [innerExpression], - op: function (context, arg) { - return runtime.getRootNode(context.me).getElementById(arg); - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - const value = elementId.value.substring(1); - return { - type: "idRef", - css: elementId.value, - value: value, - evaluate: function (context) { - return ( - runtime.getRootNode(context.me).getElementById(value) - ); - }, - }; - } - }); - - parser.addLeafExpression("classRef", function (parser, runtime, tokens) { - var classRef = tokens.matchTokenType("CLASS_REF"); - - if (!classRef) return; - if (!classRef.value) return; - - // TODO - unify these two expression types - if (classRef.template) { - var templateValue = classRef.value.substring(2); - var innerTokens = Lexer.tokenize(templateValue); - var innerExpression = parser.requireElement("expression", innerTokens); - return { - type: "classRefTemplate", - args: [innerExpression], - op: function (context, arg) { - return new ElementCollection("." + arg, context.me, true) - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - const css = classRef.value; - return { - type: "classRef", - css: css, - evaluate: function (context) { - return new ElementCollection(css, context.me, true) - }, - }; - } - }); - - class TemplatedQueryElementCollection extends ElementCollection { - constructor(css, relativeToElement, templateParts) { - super(css, relativeToElement); - this.templateParts = templateParts; - this.elements = templateParts.filter(elt => elt instanceof Element); - } - - get css() { - let rv = "", i = 0 - for (const val of this.templateParts) { - if (val instanceof Element) { - rv += "[data-hs-query-id='" + i++ + "']"; - } else rv += val; - } - return rv; - } - - [Symbol.iterator]() { - this.elements.forEach((el, i) => el.dataset.hsQueryId = i); - const rv = super[Symbol.iterator](); - this.elements.forEach(el => el.removeAttribute('data-hs-query-id')); - return rv; - } - } - - parser.addLeafExpression("queryRef", function (parser, runtime, tokens) { - var queryStart = tokens.matchOpToken("<"); - if (!queryStart) return; - var queryTokens = tokens.consumeUntil("/"); - tokens.requireOpToken("/"); - tokens.requireOpToken(">"); - var queryValue = queryTokens - .map(function (t) { - if (t.type === "STRING") { - return '"' + t.value + '"'; - } else { - return t.value; - } - }) - .join(""); - - var template, innerTokens, args; - if (queryValue.indexOf("$") >= 0) { - template = true; - innerTokens = Lexer.tokenize(queryValue, true); - args = parser.parseStringTemplate(innerTokens); - } - - return { - type: "queryRef", - css: queryValue, - args: args, - op: function (context, ...args) { - if (template) { - return new TemplatedQueryElementCollection(queryValue, context.me, args) - } else { - return new ElementCollection(queryValue, context.me) - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) { - var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF"); - if (!attributeRef) return; - if (!attributeRef.value) return; - var outerVal = attributeRef.value; - if (outerVal.indexOf("[") === 0) { - var innerValue = outerVal.substring(2, outerVal.length - 1); - } else { - var innerValue = outerVal.substring(1); - } - var css = "[" + innerValue + "]"; - var split = innerValue.split("="); - var name = split[0]; - var value = split[1]; - if (value) { - // strip quotes - if (value.indexOf('"') === 0) { - value = value.substring(1, value.length - 1); - } - } - return { - type: "attributeRef", - name: name, - css: css, - value: value, - op: function (context) { - var target = context.you || context.me; - if (target) { - return target.getAttribute(name); - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addLeafExpression("styleRef", function (parser, runtime, tokens) { - var styleRef = tokens.matchTokenType("STYLE_REF"); - if (!styleRef) return; - if (!styleRef.value) return; - var styleProp = styleRef.value.substr(1); - if (styleProp.startsWith("computed-")) { - styleProp = styleProp.substr("computed-".length); - return { - type: "computedStyleRef", - name: styleProp, - op: function (context) { - var target = context.you || context.me; - if (target) { - return runtime.resolveComputedStyle(target, styleProp); - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - return { - type: "styleRef", - name: styleProp, - op: function (context) { - var target = context.you || context.me; - if (target) { - return runtime.resolveStyle(target, styleProp); - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } - }); - - parser.addGrammarElement("objectKey", function (parser, runtime, tokens) { - var token; - if ((token = tokens.matchTokenType("STRING"))) { - return { - type: "objectKey", - key: token.value, - evaluate: function () { - return token.value; - }, - }; - } else if (tokens.matchOpToken("[")) { - var expr = parser.parseElement("expression", tokens); - tokens.requireOpToken("]"); - return { - type: "objectKey", - expr: expr, - args: [expr], - op: function (ctx, expr) { - return expr; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - var key = ""; - do { - token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-"); - if (token) key += token.value; - } while (token); - return { - type: "objectKey", - key: key, - evaluate: function () { - return key; - }, - }; - } - }); - - parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("{")) return; - var keyExpressions = []; - var valueExpressions = []; - if (!tokens.matchOpToken("}")) { - do { - var name = parser.requireElement("objectKey", tokens); - tokens.requireOpToken(":"); - var value = parser.requireElement("expression", tokens); - valueExpressions.push(value); - keyExpressions.push(name); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken("}"); - } - return { - type: "objectLiteral", - args: [keyExpressions, valueExpressions], - op: function (context, keys, values) { - var returnVal = {}; - for (var i = 0; i < keys.length; i++) { - returnVal[keys[i]] = values[i]; - } - return returnVal; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("nakedNamedArgumentList", function (parser, runtime, tokens) { - var fields = []; - var valueExpressions = []; - if (tokens.currentToken().type === "IDENTIFIER") { - do { - var name = tokens.requireTokenType("IDENTIFIER"); - tokens.requireOpToken(":"); - var value = parser.requireElement("expression", tokens); - valueExpressions.push(value); - fields.push({ name: name, value: value }); - } while (tokens.matchOpToken(",")); - } - return { - type: "namedArgumentList", - fields: fields, - args: [valueExpressions], - op: function (context, values) { - var returnVal = { _namedArgList_: true }; - for (var i = 0; i < values.length; i++) { - var field = fields[i]; - returnVal[field.name.value] = values[i]; - } - return returnVal; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("(")) return; - var elt = parser.requireElement("nakedNamedArgumentList", tokens); - tokens.requireOpToken(")"); - return elt; - }); - - parser.addGrammarElement("symbol", function (parser, runtime, tokens) { - /** @scope {SymbolScope} */ - var scope = "default"; - if (tokens.matchToken("global")) { - scope = "global"; - } else if (tokens.matchToken("element") || tokens.matchToken("module")) { - scope = "element"; - // optional possessive - if (tokens.matchOpToken("'")) { - tokens.requireToken("s"); - } - } else if (tokens.matchToken("local")) { - scope = "local"; - } - - // TODO better look ahead here - let eltPrefix = tokens.matchOpToken(":"); - let identifier = tokens.matchTokenType("IDENTIFIER"); - if (identifier && identifier.value) { - var name = identifier.value; - if (eltPrefix) { - name = ":" + name; - } - if (scope === "default") { - if (name.indexOf("$") === 0) { - scope = "global"; - } - if (name.indexOf(":") === 0) { - scope = "element"; - } - } - return { - type: "symbol", - token: identifier, - scope: scope, - name: name, - evaluate: function (context) { - return runtime.resolveSymbol(name, context, scope); - }, - }; - } - }); - - parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) { - return { - type: "implicitMeTarget", - evaluate: function (context) { - return context.you || context.me; - }, - }; - }); - - parser.addLeafExpression("boolean", function (parser, runtime, tokens) { - var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false"); - if (!booleanLiteral) return; - const value = booleanLiteral.value === "true"; - return { - type: "boolean", - evaluate: function (context) { - return value; - }, - }; - }); - - parser.addLeafExpression("null", function (parser, runtime, tokens) { - if (tokens.matchToken("null")) { - return { - type: "null", - evaluate: function (context) { - return null; - }, - }; - } - }); - - parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("[")) return; - var values = []; - if (!tokens.matchOpToken("]")) { - do { - var expr = parser.requireElement("expression", tokens); - values.push(expr); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken("]"); - } - return { - type: "arrayLiteral", - values: values, - args: [values], - op: function (context, values) { - return values; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("\\")) return; - var args = []; - var arg1 = tokens.matchTokenType("IDENTIFIER"); - if (arg1) { - args.push(arg1); - while (tokens.matchOpToken(",")) { - args.push(tokens.requireTokenType("IDENTIFIER")); - } - } - // TODO compound op token - tokens.requireOpToken("-"); - tokens.requireOpToken(">"); - var expr = parser.requireElement("expression", tokens); - return { - type: "blockLiteral", - args: args, - expr: expr, - evaluate: function (ctx) { - var returnFunc = function () { - //TODO - push scope - for (var i = 0; i < args.length; i++) { - ctx.locals[args[i].value] = arguments[i]; - } - return expr.evaluate(ctx); //OK - }; - return returnFunc; - }, - }; - }); - - parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) { - if (!tokens.matchOpToken(".")) return; - var prop = tokens.requireTokenType("IDENTIFIER"); - var propertyAccess = { - type: "propertyAccess", - root: root, - prop: prop, - args: [root], - op: function (_context, rootVal) { - var value = runtime.resolveProperty(rootVal, prop.value); - return value; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - return parser.parseElement("indirectExpression", tokens, propertyAccess); - }); - - parser.addIndirectExpression("of", function (parser, runtime, tokens, root) { - if (!tokens.matchToken("of")) return; - var newRoot = parser.requireElement("unaryExpression", tokens); - // find the urroot - var childOfUrRoot = null; - var urRoot = root; - while (urRoot.root) { - childOfUrRoot = urRoot; - urRoot = urRoot.root; - } - if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef" && urRoot.type !== "styleRef" && urRoot.type !== "computedStyleRef") { - parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type); - } - var attribute = urRoot.type === "attributeRef"; - var style = urRoot.type === "styleRef" || urRoot.type === "computedStyleRef"; - if (attribute || style) { - var attributeElt = urRoot - } - var prop = urRoot.name; - - var propertyAccess = { - type: "ofExpression", - prop: urRoot.token, - root: newRoot, - attribute: attributeElt, - expression: root, - args: [newRoot], - op: function (context, rootVal) { - if (attribute) { - return runtime.resolveAttribute(rootVal, prop); - } else if (style) { - if (urRoot.type === "computedStyleRef") { - return runtime.resolveComputedStyle(rootVal, prop); - } else { - return runtime.resolveStyle(rootVal, prop); - } - } else { - return runtime.resolveProperty(rootVal, prop); - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - - if (urRoot.type === "attributeRef") { - propertyAccess.attribute = urRoot; - } - if (childOfUrRoot) { - childOfUrRoot.root = propertyAccess; - childOfUrRoot.args = [propertyAccess]; - } else { - root = propertyAccess; - } - - return parser.parseElement("indirectExpression", tokens, root); - }); - - parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) { - if (parser.possessivesDisabled) { - return; - } - var apostrophe = tokens.matchOpToken("'"); - if ( - apostrophe || - (root.type === "symbol" && - (root.name === "my" || root.name === "its" || root.name === "your") && - (tokens.currentToken().type === "IDENTIFIER" || tokens.currentToken().type === "ATTRIBUTE_REF" || tokens.currentToken().type === "STYLE_REF")) - ) { - if (apostrophe) { - tokens.requireToken("s"); - } - - var attribute, style, prop; - attribute = parser.parseElement("attributeRef", tokens); - if (attribute == null) { - style = parser.parseElement("styleRef", tokens); - if (style == null) { - prop = tokens.requireTokenType("IDENTIFIER"); - } - } - var propertyAccess = { - type: "possessive", - root: root, - attribute: attribute || style, - prop: prop, - args: [root], - op: function (context, rootVal) { - if (attribute) { - // @ts-ignore - var value = runtime.resolveAttribute(rootVal, attribute.name); - } else if (style) { - var value - if (style.type === 'computedStyleRef') { - value = runtime.resolveComputedStyle(rootVal, style['name']); - } else { - value = runtime.resolveStyle(rootVal, style['name']); - } - } else { - var value = runtime.resolveProperty(rootVal, prop.value); - } - return value; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - return parser.parseElement("indirectExpression", tokens, propertyAccess); - } - }); - - parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) { - if (!tokens.matchToken("in")) return; - var target = parser.requireElement("unaryExpression", tokens); - var propertyAccess = { - type: "inExpression", - root: root, - args: [root, target], - op: function (context, rootVal, target) { - var returnArr = []; - if (rootVal.css) { - runtime.implicitLoop(target, function (targetElt) { - var results = targetElt.querySelectorAll(rootVal.css); - for (var i = 0; i < results.length; i++) { - returnArr.push(results[i]); - } - }); - } else if (rootVal instanceof Element) { - var within = false; - runtime.implicitLoop(target, function (targetElt) { - if (targetElt.contains(rootVal)) { - within = true; - } - }); - if(within) { - return rootVal; - } - } else { - runtime.implicitLoop(rootVal, function (rootElt) { - runtime.implicitLoop(target, function (targetElt) { - if (rootElt === targetElt) { - returnArr.push(rootElt); - } - }); - }); - } - return returnArr; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - return parser.parseElement("indirectExpression", tokens, propertyAccess); - }); - - parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) { - if (!tokens.matchToken("as")) return; - tokens.matchToken("a") || tokens.matchToken("an"); - var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise - var propertyAccess = { - type: "asExpression", - root: root, - args: [root], - op: function (context, rootVal) { - return runtime.convertValue(rootVal, conversion); - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - return parser.parseElement("indirectExpression", tokens, propertyAccess); - }); - - parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) { - if (!tokens.matchOpToken("(")) return; - var args = []; - if (!tokens.matchOpToken(")")) { - do { - args.push(parser.requireElement("expression", tokens)); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken(")"); - } - - if (root.root) { - var functionCall = { - type: "functionCall", - root: root, - argExressions: args, - args: [root.root, args], - op: function (context, rootRoot, args) { - runtime.nullCheck(rootRoot, root.root); - var func = rootRoot[root.prop.value]; - runtime.nullCheck(func, root); - if (func.hyperfunc) { - args.push(context); - } - return func.apply(rootRoot, args); - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - var functionCall = { - type: "functionCall", - root: root, - argExressions: args, - args: [root, args], - op: function (context, func, argVals) { - runtime.nullCheck(func, root); - if (func.hyperfunc) { - argVals.push(context); - } - var apply = func.apply(null, argVals); - return apply; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } - return parser.parseElement("indirectExpression", tokens, functionCall); - }); - - parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) { - var attribute = parser.parseElement("attributeRef", tokens); - if (!attribute) return; - var attributeAccess = { - type: "attributeRefAccess", - root: root, - attribute: attribute, - args: [root], - op: function (_ctx, rootVal) { - // @ts-ignore - var value = runtime.resolveAttribute(rootVal, attribute.name); - return value; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - return attributeAccess; - }); - - parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) { - if (!tokens.matchOpToken("[")) return; - var andBefore = false; - var andAfter = false; - var firstIndex = null; - var secondIndex = null; - - if (tokens.matchOpToken("..")) { - andBefore = true; - firstIndex = parser.requireElement("expression", tokens); - } else { - firstIndex = parser.requireElement("expression", tokens); - - if (tokens.matchOpToken("..")) { - andAfter = true; - var current = tokens.currentToken(); - if (current.type !== "R_BRACKET") { - secondIndex = parser.parseElement("expression", tokens); - } - } - } - tokens.requireOpToken("]"); - - var arrayIndex = { - type: "arrayIndex", - root: root, - prop: firstIndex, - firstIndex: firstIndex, - secondIndex: secondIndex, - args: [root, firstIndex, secondIndex], - op: function (_ctx, root, firstIndex, secondIndex) { - if (root == null) { - return null; - } - if (andBefore) { - if (firstIndex < 0) { - firstIndex = root.length + firstIndex; - } - return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive) - } else if (andAfter) { - if (secondIndex != null) { - if (secondIndex < 0) { - secondIndex = root.length + secondIndex; - } - return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive) - } else { - return root.slice(firstIndex); // returns from firstIndex to end of array - } - } else { - return root[firstIndex]; - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - - return parser.parseElement("indirectExpression", tokens, arrayIndex); - }); - - // taken from https://drafts.csswg.org/css-values-4/#relative-length - // and https://drafts.csswg.org/css-values-4/#absolute-length - // (NB: we do not support `in` dues to conflicts w/ the hyperscript grammar) - var STRING_POSTFIXES = [ - 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax', - 'cm', 'mm', 'Q', 'pc', 'pt', 'px' - ]; - parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) { - var root = parser.parseElement("primaryExpression", tokens); - - let stringPosfix = tokens.matchAnyToken.apply(tokens, STRING_POSTFIXES) || tokens.matchOpToken("%"); - if (stringPosfix) { - return { - type: "stringPostfix", - postfix: stringPosfix.value, - args: [root], - op: function (context, val) { - return "" + val + stringPosfix.value; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } - - var timeFactor = null; - if (tokens.matchToken("s") || tokens.matchToken("seconds")) { - timeFactor = 1000; - } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) { - timeFactor = 1; - } - if (timeFactor) { - return { - type: "timeExpression", - time: root, - factor: timeFactor, - args: [root], - op: function (_context, val) { - return val * timeFactor; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } - - if (tokens.matchOpToken(":")) { - var typeName = tokens.requireTokenType("IDENTIFIER"); - if (!typeName.value) return; - var nullOk = !tokens.matchOpToken("!"); - return { - type: "typeCheck", - typeName: typeName, - nullOk: nullOk, - args: [root], - op: function (context, val) { - var passed = runtime.typeCheck(val, this.typeName.value, nullOk); - if (passed) { - return val; - } else { - throw new Error("Typecheck failed! Expected: " + typeName.value); - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } else { - return root; - } - }); - - parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) { - if (!tokens.matchToken("not")) return; - var root = parser.requireElement("unaryExpression", tokens); - return { - type: "logicalNot", - root: root, - args: [root], - op: function (context, val) { - return !val; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("noExpression", function (parser, runtime, tokens) { - if (!tokens.matchToken("no")) return; - var root = parser.requireElement("unaryExpression", tokens); - return { - type: "noExpression", - root: root, - args: [root], - op: function (_context, val) { - return runtime.isEmpty(val); - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addLeafExpression("some", function (parser, runtime, tokens) { - if (!tokens.matchToken("some")) return; - var root = parser.requireElement("expression", tokens); - return { - type: "noExpression", - root: root, - args: [root], - op: function (_context, val) { - return !runtime.isEmpty(val); - }, - evaluate(context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("-")) return; - var root = parser.requireElement("unaryExpression", tokens); - return { - type: "negativeNumber", - root: root, - args: [root], - op: function (context, value) { - return -1 * value; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) { - tokens.matchToken("the"); // optional "the" - return parser.parseAnyOf( - ["beepExpression", "logicalNot", "relativePositionalExpression", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"], - tokens - ); - }); - - parser.addGrammarElement("beepExpression", function (parser, runtime, tokens) { - if (!tokens.matchToken("beep!")) return; - var expression = parser.parseElement("unaryExpression", tokens); - if (expression) { - expression['booped'] = true; - var originalEvaluate = expression.evaluate; - expression.evaluate = function(ctx){ - let value = originalEvaluate.apply(expression, arguments); - let element = ctx.me; - runtime.beepValueToConsole(element, expression, value); - return value; - } - return expression; - } - }); - - var scanForwardQuery = function(start, root, match, wrap) { - var results = root.querySelectorAll(match); - for (var i = 0; i < results.length; i++) { - var elt = results[i]; - if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { - return elt; - } - } - if (wrap) { - return results[0]; - } - } - - var scanBackwardsQuery = function(start, root, match, wrap) { - var results = root.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; - } - } - if (wrap) { - return results[results.length - 1]; - } - } - - var scanForwardArray = function(start, array, match, wrap) { - var matches = []; - Runtime.prototype.forEach(array, function(elt){ - if (elt.matches(match) || elt === start) { - matches.push(elt); - } - }) - for (var i = 0; i < matches.length - 1; i++) { - var elt = matches[i]; - if (elt === start) { - return matches[i + 1]; - } - } - if (wrap) { - var first = matches[0]; - if (first && first.matches(match)) { - return first; - } - } - } - - var scanBackwardsArray = function(start, array, match, wrap) { - return scanForwardArray(start, Array.from(array).reverse(), match, wrap); - } - - parser.addGrammarElement("relativePositionalExpression", function (parser, runtime, tokens) { - var op = tokens.matchAnyToken("next", "previous"); - if (!op) return; - var forwardSearch = op.value === "next"; - - var thingElt = parser.parseElement("expression", tokens); - - if (tokens.matchToken("from")) { - tokens.pushFollow("in"); - try { - var from = parser.requireElement("unaryExpression", tokens); - } finally { - tokens.popFollow(); - } - } else { - var from = parser.requireElement("implicitMeTarget", tokens); - } - - var inSearch = false; - var withinElt; - if (tokens.matchToken("in")) { - inSearch = true; - var inElt = parser.requireElement("unaryExpression", tokens); - } else if (tokens.matchToken("within")) { - withinElt = parser.requireElement("unaryExpression", tokens); - } else { - withinElt = document.body; - } - - var wrapping = false; - if (tokens.matchToken("with")) { - tokens.requireToken("wrapping") - wrapping = true; - } - - return { - type: "relativePositionalExpression", - from: from, - forwardSearch: forwardSearch, - inSearch: inSearch, - wrapping: wrapping, - inElt: inElt, - withinElt: withinElt, - operator: op.value, - args: [thingElt, from, inElt, withinElt], - op: function (context, thing, from, inElt, withinElt) { - - var css = thing.css; - if (css == null) { - throw "Expected a CSS value to be returned by " + Tokens.sourceFor.apply(thingElt); - } - - if(inSearch) { - if (inElt) { - if (forwardSearch) { - return scanForwardArray(from, inElt, css, wrapping); - } else { - return scanBackwardsArray(from, inElt, css, wrapping); - } - } - } else { - if (withinElt) { - if (forwardSearch) { - return scanForwardQuery(from, withinElt, css, wrapping); - } else { - return scanBackwardsQuery(from, withinElt, css, wrapping); - } - } - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - } - - }); - - parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) { - var op = tokens.matchAnyToken("first", "last", "random"); - if (!op) return; - tokens.matchAnyToken("in", "from", "of"); - var rhs = parser.requireElement("unaryExpression", tokens); - const operator = op.value; - return { - type: "positionalExpression", - rhs: rhs, - operator: op.value, - args: [rhs], - op: function (context, rhsVal) { - if (rhsVal && !Array.isArray(rhsVal)) { - if (rhsVal.children) { - rhsVal = rhsVal.children; - } else { - rhsVal = Array.from(rhsVal); - } - } - if (rhsVal) { - if (operator === "first") { - return rhsVal[0]; - } else if (operator === "last") { - return rhsVal[rhsVal.length - 1]; - } else if (operator === "random") { - return rhsVal[Math.floor(Math.random() * rhsVal.length)]; - } - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - }); - - parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) { - var expr = parser.parseElement("unaryExpression", tokens); - var mathOp, - initialMathOp = null; - mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod'); - while (mathOp) { - initialMathOp = initialMathOp || mathOp; - var operator = mathOp.value; - if (initialMathOp.value !== operator) { - parser.raiseParseError(tokens, "You must parenthesize math operations with different operators"); - } - var rhs = parser.parseElement("unaryExpression", tokens); - expr = { - type: "mathOperator", - lhs: expr, - rhs: rhs, - operator: operator, - args: [expr, rhs], - op: function (context, lhsVal, rhsVal) { - if (operator === "+") { - return lhsVal + rhsVal; - } else if (operator === "-") { - return lhsVal - rhsVal; - } else if (operator === "*") { - return lhsVal * rhsVal; - } else if (operator === "/") { - return lhsVal / rhsVal; - } else if (operator === "mod") { - return lhsVal % rhsVal; - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod'); - } - return expr; - }); - - parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) { - return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens); - }); - - function sloppyContains(src, container, value){ - if (container['contains']) { - return container.contains(value); - } else if (container['includes']) { - return container.includes(value); - } else { - throw Error("The value of " + src.sourceFor() + " does not have a contains or includes method on it"); - } - } - function sloppyMatches(src, target, toMatch){ - if (target['match']) { - return !!target.match(toMatch); - } else if (target['matches']) { - return target.matches(toMatch); - } else { - throw Error("The value of " + src.sourceFor() + " does not have a match or matches method on it"); - } - } - - parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) { - var expr = parser.parseElement("mathExpression", tokens); - var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!=="); - var operator = comparisonToken ? comparisonToken.value : null; - var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions. - var typeCheck = false; - - if (operator == null) { - if (tokens.matchToken("is") || tokens.matchToken("am")) { - if (tokens.matchToken("not")) { - if (tokens.matchToken("in")) { - operator = "not in"; - } else if (tokens.matchToken("a")) { - operator = "not a"; - typeCheck = true; - } else if (tokens.matchToken("empty")) { - operator = "not empty"; - hasRightValue = false; - } else { - if (tokens.matchToken("really")) { - operator = "!=="; - } else { - operator = "!="; - } - // consume additional optional syntax - if (tokens.matchToken("equal")) { - tokens.matchToken("to"); - } - } - } else if (tokens.matchToken("in")) { - operator = "in"; - } else if (tokens.matchToken("a")) { - operator = "a"; - typeCheck = true; - } else if (tokens.matchToken("empty")) { - operator = "empty"; - hasRightValue = false; - } else if (tokens.matchToken("less")) { - tokens.requireToken("than"); - if (tokens.matchToken("or")) { - tokens.requireToken("equal"); - tokens.requireToken("to"); - operator = "<="; - } else { - operator = "<"; - } - } else if (tokens.matchToken("greater")) { - tokens.requireToken("than"); - if (tokens.matchToken("or")) { - tokens.requireToken("equal"); - tokens.requireToken("to"); - operator = ">="; - } else { - operator = ">"; - } - } else { - if (tokens.matchToken("really")) { - operator = "==="; - } else { - operator = "=="; - } - if (tokens.matchToken("equal")) { - tokens.matchToken("to"); - } - } - } else if (tokens.matchToken("equals")) { - operator = "=="; - } else if (tokens.matchToken("really")) { - tokens.requireToken("equals") - operator = "==="; - } else if (tokens.matchToken("exist") || tokens.matchToken("exists")) { - operator = "exist"; - hasRightValue = false; - } else if (tokens.matchToken("matches") || tokens.matchToken("match")) { - operator = "match"; - } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { - operator = "contain"; - } else if (tokens.matchToken("includes") || tokens.matchToken("include")) { - operator = "include"; - } else if (tokens.matchToken("do") || tokens.matchToken("does")) { - tokens.requireToken("not"); - if (tokens.matchToken("matches") || tokens.matchToken("match")) { - operator = "not match"; - } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { - operator = "not contain"; - } else if (tokens.matchToken("exist") || tokens.matchToken("exist")) { - operator = "not exist"; - hasRightValue = false; - } else if (tokens.matchToken("include")) { - operator = "not include"; - } else { - parser.raiseParseError(tokens, "Expected matches or contains"); - } - } - } - - if (operator) { - // Do not allow chained comparisons, which is dumb - var typeName, nullOk, rhs - if (typeCheck) { - typeName = tokens.requireTokenType("IDENTIFIER"); - nullOk = !tokens.matchOpToken("!"); - } else if (hasRightValue) { - rhs = parser.requireElement("mathExpression", tokens); - if (operator === "match" || operator === "not match") { - rhs = rhs.css ? rhs.css : rhs; - } - } - var lhs = expr; - expr = { - type: "comparisonOperator", - operator: operator, - typeName: typeName, - nullOk: nullOk, - lhs: expr, - rhs: rhs, - args: [expr, rhs], - op: function (context, lhsVal, rhsVal) { - if (operator === "==") { - return lhsVal == rhsVal; - } else if (operator === "!=") { - return lhsVal != rhsVal; - } - if (operator === "===") { - return lhsVal === rhsVal; - } else if (operator === "!==") { - return lhsVal !== rhsVal; - } - if (operator === "match") { - return lhsVal != null && sloppyMatches(lhs, lhsVal, rhsVal); - } - if (operator === "not match") { - return lhsVal == null || !sloppyMatches(lhs, lhsVal, rhsVal); - } - if (operator === "in") { - return rhsVal != null && sloppyContains(rhs, rhsVal, lhsVal); - } - if (operator === "not in") { - return rhsVal == null || !sloppyContains(rhs, rhsVal, lhsVal); - } - if (operator === "contain") { - return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal); - } - if (operator === "not contain") { - return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal); - } - if (operator === "include") { - return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal); - } - if (operator === "not include") { - return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal); - } - if (operator === "===") { - return lhsVal === rhsVal; - } else if (operator === "!==") { - return lhsVal !== rhsVal; - } else if (operator === "<") { - return lhsVal < rhsVal; - } else if (operator === ">") { - return lhsVal > rhsVal; - } else if (operator === "<=") { - return lhsVal <= rhsVal; - } else if (operator === ">=") { - return lhsVal >= rhsVal; - } else if (operator === "empty") { - return runtime.isEmpty(lhsVal); - } else if (operator === "not empty") { - return !runtime.isEmpty(lhsVal); - } else if (operator === "exist") { - return runtime.doesExist(lhsVal); - } else if (operator === "not exist") { - return !runtime.doesExist(lhsVal); - } else if (operator === "a") { - return runtime.typeCheck(lhsVal, typeName.value, nullOk); - } else if (operator === "not a") { - return !runtime.typeCheck(lhsVal, typeName.value, nullOk); - } else { - throw "Unknown comparison : " + operator; - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - } - return expr; - }); - - parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) { - return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens); - }); - - parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) { - var expr = parser.parseElement("comparisonExpression", tokens); - var logicalOp, - initialLogicalOp = null; - logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); - while (logicalOp) { - initialLogicalOp = initialLogicalOp || logicalOp; - if (initialLogicalOp.value !== logicalOp.value) { - parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators"); - } - var rhs = parser.requireElement("comparisonExpression", tokens); - const operator = logicalOp.value; - expr = { - type: "logicalOperator", - operator: operator, - lhs: expr, - rhs: rhs, - args: [expr, rhs], - op: function (context, lhsVal, rhsVal) { - if (operator === "and") { - return lhsVal && rhsVal; - } else { - return lhsVal || rhsVal; - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); - } - return expr; - }); - - parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) { - return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens); - }); - - parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) { - if (tokens.matchToken("async")) { - var value = parser.requireElement("logicalExpression", tokens); - var expr = { - type: "asyncExpression", - value: value, - evaluate: function (context) { - return { - asyncWrapper: true, - value: this.value.evaluate(context), //OK - }; - }, - }; - return expr; - } else { - return parser.parseElement("logicalExpression", tokens); - } - }); - - parser.addGrammarElement("expression", function (parser, runtime, tokens) { - tokens.matchToken("the"); // optional the - return parser.parseElement("asyncExpression", tokens); - }); - - parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) { - tokens.matchToken("the"); // optional the - - // TODO obviously we need to generalize this as a left hand side / targetable concept - var expr = parser.parseElement("primaryExpression", tokens); - if (expr && ( - expr.type === "symbol" || - expr.type === "ofExpression" || - expr.type === "propertyAccess" || - expr.type === "attributeRefAccess" || - expr.type === "attributeRef" || - expr.type === "styleRef" || - expr.type === "arrayIndex" || - expr.type === "possessive") - ) { - return expr; - } else { - parser.raiseParseError( - tokens, - "A target expression must be writable. The expression type '" + (expr && expr.type) + "' is not." - ); - } - return expr; - }); - - parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) { - var features = []; - - if (tokens.hasMore()) { - while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") { - var feature = parser.requireElement("feature", tokens); - features.push(feature); - tokens.matchToken("end"); // optional end - } - } - return { - type: "hyperscript", - features: features, - apply: function (target, source, args) { - // no op - for (const feature of features) { - feature.install(target, source, args); - } - }, - }; - }); - - var parseEventArgs = function (tokens) { - var args = []; - // handle argument list (look ahead 3) - if ( - tokens.token(0).value === "(" && - (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")") - ) { - tokens.matchOpToken("("); - do { - args.push(tokens.requireTokenType("IDENTIFIER")); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken(")"); - } - return args; - }; - - parser.addFeature("on", function (parser, runtime, tokens) { - if (!tokens.matchToken("on")) return; - var every = false; - if (tokens.matchToken("every")) { - every = true; - } - var events = []; - var displayName = null; - do { - var on = parser.requireElement("eventName", tokens, "Expected event name"); - - var eventName = on.evaluate(); // OK No Promise - - if (displayName) { - displayName = displayName + " or " + eventName; - } else { - displayName = "on " + eventName; - } - var args = parseEventArgs(tokens); - - var filter = null; - if (tokens.matchOpToken("[")) { - filter = parser.requireElement("expression", tokens); - tokens.requireOpToken("]"); - } - - var startCount, endCount ,unbounded; - if (tokens.currentToken().type === "NUMBER") { - var startCountToken = tokens.consumeToken(); - if (!startCountToken.value) return; - startCount = parseInt(startCountToken.value); - if (tokens.matchToken("to")) { - var endCountToken = tokens.consumeToken(); - if (!endCountToken.value) return; - endCount = parseInt(endCountToken.value); - } else if (tokens.matchToken("and")) { - unbounded = true; - tokens.requireToken("on"); - } - } - - var intersectionSpec, mutationSpec; - if (eventName === "intersection") { - intersectionSpec = {}; - if (tokens.matchToken("with")) { - intersectionSpec["with"] = parser.requireElement("expression", tokens).evaluate(); - } - if (tokens.matchToken("having")) { - do { - if (tokens.matchToken("margin")) { - intersectionSpec["rootMargin"] = parser.requireElement("stringLike", tokens).evaluate(); - } else if (tokens.matchToken("threshold")) { - intersectionSpec["threshold"] = parser.requireElement("expression", tokens).evaluate(); - } else { - parser.raiseParseError(tokens, "Unknown intersection config specification"); - } - } while (tokens.matchToken("and")); - } - } else if (eventName === "mutation") { - mutationSpec = {}; - if (tokens.matchToken("of")) { - do { - if (tokens.matchToken("anything")) { - mutationSpec["attributes"] = true; - mutationSpec["subtree"] = true; - mutationSpec["characterData"] = true; - mutationSpec["childList"] = true; - } else if (tokens.matchToken("childList")) { - mutationSpec["childList"] = true; - } else if (tokens.matchToken("attributes")) { - mutationSpec["attributes"] = true; - mutationSpec["attributeOldValue"] = true; - } else if (tokens.matchToken("subtree")) { - mutationSpec["subtree"] = true; - } else if (tokens.matchToken("characterData")) { - mutationSpec["characterData"] = true; - mutationSpec["characterDataOldValue"] = true; - } else if (tokens.currentToken().type === "ATTRIBUTE_REF") { - var attribute = tokens.consumeToken(); - if (mutationSpec["attributeFilter"] == null) { - mutationSpec["attributeFilter"] = []; - } - if (attribute.value.indexOf("@") == 0) { - mutationSpec["attributeFilter"].push(attribute.value.substring(1)); - } else { - parser.raiseParseError( - tokens, - "Only shorthand attribute references are allowed here" - ); - } - } else { - parser.raiseParseError(tokens, "Unknown mutation config specification"); - } - } while (tokens.matchToken("or")); - } else { - mutationSpec["attributes"] = true; - mutationSpec["characterData"] = true; - mutationSpec["childList"] = true; - } - } - - var from = null; - var elsewhere = false; - if (tokens.matchToken("from")) { - if (tokens.matchToken("elsewhere")) { - elsewhere = true; - } else { - tokens.pushFollow("or"); - try { - from = parser.requireElement("expression", tokens) - } finally { - tokens.popFollow(); - } - if (!from) { - parser.raiseParseError(tokens, 'Expected either target value or "elsewhere".'); - } - } - } - // support both "elsewhere" and "from elsewhere" - if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) { - elsewhere = true; - } - - if (tokens.matchToken("in")) { - var inExpr = parser.parseElement('unaryExpression', tokens); - } - - if (tokens.matchToken("debounced")) { - tokens.requireToken("at"); - var timeExpr = parser.requireElement("unaryExpression", tokens); - // @ts-ignore - var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr - } else if (tokens.matchToken("throttled")) { - tokens.requireToken("at"); - var timeExpr = parser.requireElement("unaryExpression", tokens); - // @ts-ignore - var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr - } - - events.push({ - execCount: 0, - every: every, - on: eventName, - args: args, - filter: filter, - from: from, - inExpr: inExpr, - elsewhere: elsewhere, - startCount: startCount, - endCount: endCount, - unbounded: unbounded, - debounceTime: debounceTime, - throttleTime: throttleTime, - mutationSpec: mutationSpec, - intersectionSpec: intersectionSpec, - debounced: undefined, - lastExec: undefined, - }); - } while (tokens.matchToken("or")); - - var queueLast = true; - if (!every) { - if (tokens.matchToken("queue")) { - if (tokens.matchToken("all")) { - var queueAll = true; - var queueLast = false; - } else if (tokens.matchToken("first")) { - var queueFirst = true; - } else if (tokens.matchToken("none")) { - var queueNone = true; - } else { - tokens.requireToken("last"); - } - } - } - - var start = parser.requireElement("commandList", tokens); - parser.ensureTerminated(start); - - var errorSymbol, errorHandler; - if (tokens.matchToken("catch")) { - errorSymbol = tokens.requireTokenType("IDENTIFIER").value; - errorHandler = parser.requireElement("commandList", tokens); - parser.ensureTerminated(errorHandler); - } - - if (tokens.matchToken("finally")) { - var finallyHandler = parser.requireElement("commandList", tokens); - parser.ensureTerminated(finallyHandler); - } - - var onFeature = { - displayName: displayName, - events: events, - start: start, - every: every, - execCount: 0, - errorHandler: errorHandler, - errorSymbol: errorSymbol, - execute: function (/** @type {Context} */ ctx) { - let eventQueueInfo = runtime.getEventQueueFor(ctx.me, onFeature); - if (eventQueueInfo.executing && every === false) { - if (queueNone || (queueFirst && eventQueueInfo.queue.length > 0)) { - return; - } - if (queueLast) { - eventQueueInfo.queue.length = 0; - } - eventQueueInfo.queue.push(ctx); - return; - } - onFeature.execCount++; - eventQueueInfo.executing = true; - ctx.meta.onHalt = function () { - eventQueueInfo.executing = false; - var queued = eventQueueInfo.queue.shift(); - if (queued) { - setTimeout(function () { - onFeature.execute(queued); - }, 1); - } - }; - ctx.meta.reject = function (err) { - console.error(err.message ? err.message : err); - var hypertrace = runtime.getHyperTrace(ctx, err); - if (hypertrace) { - hypertrace.print(); - } - runtime.triggerEvent(ctx.me, "exception", { - error: err, - }); - }; - start.execute(ctx); - }, - install: function (elt, source) { - for (const eventSpec of onFeature.events) { - var targets; - if (eventSpec.elsewhere) { - targets = [document]; - } else if (eventSpec.from) { - targets = eventSpec.from.evaluate(runtime.makeContext(elt, onFeature, elt, null)); - } else { - targets = [elt]; - } - runtime.implicitLoop(targets, function (target) { - // OK NO PROMISE - - var eventName = eventSpec.on; - if (target == null) { - console.warn("'%s' feature ignored because target does not exists:", displayName, elt); - return; - } - - if (eventSpec.mutationSpec) { - eventName = "hyperscript:mutation"; - const observer = new MutationObserver(function (mutationList, observer) { - if (!onFeature.executing) { - runtime.triggerEvent(target, eventName, { - mutationList: mutationList, - observer: observer, - }); - } - }); - observer.observe(target, eventSpec.mutationSpec); - } - - if (eventSpec.intersectionSpec) { - eventName = "hyperscript:intersection"; - const observer = new IntersectionObserver(function (entries) { - for (const entry of entries) { - var detail = { - observer: observer, - }; - detail = Object.assign(detail, entry); - detail["intersecting"] = entry.isIntersecting; - runtime.triggerEvent(target, eventName, detail); - } - }, eventSpec.intersectionSpec); - observer.observe(target); - } - - var addEventListener = target.addEventListener || target.on; - addEventListener.call(target, eventName, function listener(evt) { - // OK NO PROMISE - if (typeof Node !== 'undefined' && elt instanceof Node && target !== elt && !elt.isConnected) { - target.removeEventListener(eventName, listener); - return; - } - - var ctx = runtime.makeContext(elt, onFeature, elt, evt); - if (eventSpec.elsewhere && elt.contains(evt.target)) { - return; - } - if (eventSpec.from) { - ctx.result = target; - } - - // establish context - for (const arg of eventSpec.args) { - let eventValue = ctx.event[arg.value]; - if (eventValue !== undefined) { - ctx.locals[arg.value] = eventValue; - } else if ('detail' in ctx.event) { - ctx.locals[arg.value] = ctx.event['detail'][arg.value]; - } - } - - // install error handler if any - ctx.meta.errorHandler = errorHandler; - ctx.meta.errorSymbol = errorSymbol; - ctx.meta.finallyHandler = finallyHandler; - - // apply filter - if (eventSpec.filter) { - var initialCtx = ctx.meta.context; - ctx.meta.context = ctx.event; - try { - var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE - if (value) { - // match the javascript semantics for if statements - } else { - return; - } - } finally { - ctx.meta.context = initialCtx; - } - } - - if (eventSpec.inExpr) { - var inElement = evt.target; - while (true) { - if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) { - ctx.result = inElement; - break; - } else { - inElement = inElement.parentElement; - if (inElement == null) { - return; // no match found - } - } - } - } - - // verify counts - eventSpec.execCount++; - if (eventSpec.startCount) { - if (eventSpec.endCount) { - if ( - eventSpec.execCount < eventSpec.startCount || - eventSpec.execCount > eventSpec.endCount - ) { - return; - } - } else if (eventSpec.unbounded) { - if (eventSpec.execCount < eventSpec.startCount) { - return; - } - } else if (eventSpec.execCount !== eventSpec.startCount) { - return; - } - } - - //debounce - if (eventSpec.debounceTime) { - if (eventSpec.debounced) { - clearTimeout(eventSpec.debounced); - } - eventSpec.debounced = setTimeout(function () { - onFeature.execute(ctx); - }, eventSpec.debounceTime); - return; - } - - // throttle - if (eventSpec.throttleTime) { - if ( - eventSpec.lastExec && - Date.now() < (eventSpec.lastExec + eventSpec.throttleTime) - ) { - return; - } else { - eventSpec.lastExec = Date.now(); - } - } - - // apply execute - onFeature.execute(ctx); - }); - }); - } - }, - }; - parser.setParent(start, onFeature); - return onFeature; - }); - - parser.addFeature("def", function (parser, runtime, tokens) { - if (!tokens.matchToken("def")) return; - var functionName = parser.requireElement("dotOrColonPath", tokens); - var nameVal = functionName.evaluate(); // OK - var nameSpace = nameVal.split("."); - var funcName = nameSpace.pop(); - - var args = []; - if (tokens.matchOpToken("(")) { - if (tokens.matchOpToken(")")) { - // emtpy args list - } else { - do { - args.push(tokens.requireTokenType("IDENTIFIER")); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken(")"); - } - } - - var start = parser.requireElement("commandList", tokens); - - var errorSymbol, errorHandler; - if (tokens.matchToken("catch")) { - errorSymbol = tokens.requireTokenType("IDENTIFIER").value; - errorHandler = parser.parseElement("commandList", tokens); - } - - if (tokens.matchToken("finally")) { - var finallyHandler = parser.requireElement("commandList", tokens); - parser.ensureTerminated(finallyHandler); - } - - var functionFeature = { - displayName: - funcName + - "(" + - args - .map(function (arg) { - return arg.value; - }) - .join(", ") + - ")", - name: funcName, - args: args, - start: start, - errorHandler: errorHandler, - errorSymbol: errorSymbol, - finallyHandler: finallyHandler, - install: function (target, source) { - var func = function () { - // null, worker - var ctx = runtime.makeContext(source, functionFeature, target, null); - - // install error handler if any - ctx.meta.errorHandler = errorHandler; - ctx.meta.errorSymbol = errorSymbol; - ctx.meta.finallyHandler = finallyHandler; - - for (var i = 0; i < args.length; i++) { - var name = args[i]; - var argumentVal = arguments[i]; - if (name) { - ctx.locals[name.value] = argumentVal; - } - } - ctx.meta.caller = arguments[args.length]; - if (ctx.meta.caller) { - ctx.meta.callingCommand = ctx.meta.caller.meta.command; - } - var resolve, - reject = null; - var promise = new Promise(function (theResolve, theReject) { - resolve = theResolve; - reject = theReject; - }); - start.execute(ctx); - if (ctx.meta.returned) { - return ctx.meta.returnValue; - } else { - ctx.meta.resolve = resolve; - ctx.meta.reject = reject; - return promise; - } - }; - func.hyperfunc = true; - func.hypername = nameVal; - runtime.assignToNamespace(target, nameSpace, funcName, func); - }, - }; - - parser.ensureTerminated(start); - - // terminate error handler if any - if (errorHandler) { - parser.ensureTerminated(errorHandler); - } - - parser.setParent(start, functionFeature); - return functionFeature; - }); - - parser.addFeature("set", function (parser, runtime, tokens) { - let setCmd = parser.parseElement("setCommand", tokens); - if (setCmd) { - if (setCmd.target.scope !== "element") { - parser.raiseParseError(tokens, "variables declared at the feature level must be element scoped."); - } - let setFeature = { - start: setCmd, - install: function (target, source) { - setCmd && setCmd.execute(runtime.makeContext(target, setFeature, target, null)); - }, - }; - parser.ensureTerminated(setCmd); - return setFeature; - } - }); - - parser.addFeature("init", function (parser, runtime, tokens) { - if (!tokens.matchToken("init")) return; - - var immediately = tokens.matchToken("immediately"); - - var start = parser.requireElement("commandList", tokens); - var initFeature = { - start: start, - install: function (target, source) { - let handler = function () { - start && start.execute(runtime.makeContext(target, initFeature, target, null)); - }; - if (immediately) { - handler(); - } else { - setTimeout(handler, 0); - } - }, - }; - - // terminate body - parser.ensureTerminated(start); - parser.setParent(start, initFeature); - return initFeature; - }); - - parser.addFeature("worker", function (parser, runtime, tokens) { - if (tokens.matchToken("worker")) { - parser.raiseParseError( - tokens, - "In order to use the 'worker' feature, include " + - "the _hyperscript worker plugin. See " + - "https://hyperscript.org/features/worker/ for " + - "more info." - ); - return undefined - } - }); - - parser.addFeature("behavior", function (parser, runtime, tokens) { - if (!tokens.matchToken("behavior")) return; - var path = parser.requireElement("dotOrColonPath", tokens).evaluate(); - var nameSpace = path.split("."); - var name = nameSpace.pop(); - - var formalParams = []; - if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) { - do { - formalParams.push(tokens.requireTokenType("IDENTIFIER").value); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken(")"); - } - var hs = parser.requireElement("hyperscript", tokens); - for (var i = 0; i < hs.features.length; i++) { - var feature = hs.features[i]; - feature.behavior = path; - } - - return { - install: function (target, source) { - runtime.assignToNamespace( - globalScope.document && globalScope.document.body, - nameSpace, - name, - function (target, source, innerArgs) { - var internalData = runtime.getInternalData(target); - var elementScope = getOrInitObject(internalData, path + "Scope"); - for (var i = 0; i < formalParams.length; i++) { - elementScope[formalParams[i]] = innerArgs[formalParams[i]]; - } - hs.apply(target, source); - } - ); - }, - }; - }); - - parser.addFeature("install", function (parser, runtime, tokens) { - if (!tokens.matchToken("install")) return; - var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate(); - var behaviorNamespace = behaviorPath.split("."); - var args = parser.parseElement("namedArgumentList", tokens); - - var installFeature; - return (installFeature = { - install: function (target, source) { - runtime.unifiedEval( - { - args: [args], - op: function (ctx, args) { - var behavior = globalScope; - for (var i = 0; i < behaviorNamespace.length; i++) { - behavior = behavior[behaviorNamespace[i]]; - if (typeof behavior !== "object" && typeof behavior !== "function") - throw new Error("No such behavior defined as " + behaviorPath); - } - - if (!(behavior instanceof Function)) - throw new Error(behaviorPath + " is not a behavior"); - - behavior(target, source, args); - }, - }, - runtime.makeContext(target, installFeature, target, null) - ); - }, - }); - }); - - parser.addGrammarElement("jsBody", function (parser, runtime, tokens) { - var jsSourceStart = tokens.currentToken().start; - var jsLastToken = tokens.currentToken(); - - var funcNames = []; - var funcName = ""; - var expectFunctionDeclaration = false; - while (tokens.hasMore()) { - jsLastToken = tokens.consumeToken(); - var peek = tokens.token(0, true); - if (peek.type === "IDENTIFIER" && peek.value === "end") { - break; - } - if (expectFunctionDeclaration) { - if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") { - funcName += jsLastToken.value; - } else { - if (funcName !== "") funcNames.push(funcName); - funcName = ""; - expectFunctionDeclaration = false; - } - } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") { - expectFunctionDeclaration = true; - } - } - var jsSourceEnd = jsLastToken.end + 1; - - return { - type: "jsBody", - exposedFunctionNames: funcNames, - jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd), - }; - }); - - parser.addFeature("js", function (parser, runtime, tokens) { - if (!tokens.matchToken("js")) return; - var jsBody = parser.requireElement("jsBody", tokens); - - var jsSource = - jsBody.jsSource + - "\nreturn { " + - jsBody.exposedFunctionNames - .map(function (name) { - return name + ":" + name; - }) - .join(",") + - " } "; - var func = new Function(jsSource); - - return { - jsSource: jsSource, - function: func, - exposedFunctionNames: jsBody.exposedFunctionNames, - install: function () { - Object.assign(globalScope, func()); - }, - }; - }); - - parser.addCommand("js", function (parser, runtime, tokens) { - if (!tokens.matchToken("js")) return; - // Parse inputs - var inputs = []; - if (tokens.matchOpToken("(")) { - if (tokens.matchOpToken(")")) { - // empty input list - } else { - do { - var inp = tokens.requireTokenType("IDENTIFIER"); - inputs.push(inp.value); - } while (tokens.matchOpToken(",")); - tokens.requireOpToken(")"); - } - } - - var jsBody = parser.requireElement("jsBody", tokens); - tokens.matchToken("end"); - - var func = varargConstructor(Function, inputs.concat([jsBody.jsSource])); - - var command = { - jsSource: jsBody.jsSource, - function: func, - inputs: inputs, - op: function (context) { - var args = []; - inputs.forEach(function (input) { - args.push(runtime.resolveSymbol(input, context, 'default')); - }); - var result = func.apply(globalScope, args); - if (result && typeof result.then === "function") { - return new Promise(function (resolve) { - result.then(function (actualResult) { - context.result = actualResult; - resolve(runtime.findNext(this, context)); - }); - }); - } else { - context.result = result; - return runtime.findNext(this, context); - } - }, - }; - return command; - }); - - parser.addCommand("async", function (parser, runtime, tokens) { - if (!tokens.matchToken("async")) return; - if (tokens.matchToken("do")) { - var body = parser.requireElement("commandList", tokens); - - // Append halt - var end = body; - while (end.next) end = end.next; - end.next = runtime.HALT; - - tokens.requireToken("end"); - } else { - var body = parser.requireElement("command", tokens); - } - var command = { - body: body, - op: function (context) { - setTimeout(function () { - body.execute(context); - }); - return runtime.findNext(this, context); - }, - }; - parser.setParent(body, command); - return command; - }); - - parser.addCommand("tell", function (parser, runtime, tokens) { - var startToken = tokens.currentToken(); - if (!tokens.matchToken("tell")) return; - var value = parser.requireElement("expression", tokens); - var body = parser.requireElement("commandList", tokens); - if (tokens.hasMore() && !parser.featureStart(tokens.currentToken())) { - tokens.requireToken("end"); - } - var slot = "tell_" + startToken.start; - var tellCmd = { - value: value, - body: body, - args: [value], - resolveNext: function (context) { - var iterator = context.meta.iterators[slot]; - if (iterator.index < iterator.value.length) { - context.you = iterator.value[iterator.index++]; - return body; - } else { - // restore original me - context.you = iterator.originalYou; - if (this.next) { - return this.next; - } else { - return runtime.findNext(this.parent, context); - } - } - }, - op: function (context, value) { - if (value == null) { - value = []; - } else if (!(Array.isArray(value) || value instanceof NodeList)) { - value = [value]; - } - context.meta.iterators[slot] = { - originalYou: context.you, - index: 0, - value: value, - }; - return this.resolveNext(context); - }, - }; - parser.setParent(body, tellCmd); - return tellCmd; - }); - - parser.addCommand("wait", function (parser, runtime, tokens) { - if (!tokens.matchToken("wait")) return; - var command; - - // wait on event - if (tokens.matchToken("for")) { - tokens.matchToken("a"); // optional "a" - var events = []; - do { - var lookahead = tokens.token(0); - if (lookahead.type === 'NUMBER' || lookahead.type === 'L_PAREN') { - events.push({ - time: parser.requireElement('expression', tokens).evaluate() // TODO: do we want to allow async here? - }) - } else { - events.push({ - name: parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(), - args: parseEventArgs(tokens), - }); - } - } while (tokens.matchToken("or")); - - if (tokens.matchToken("from")) { - var on = parser.requireElement("expression", tokens); - } - - // wait on event - command = { - event: events, - on: on, - args: [on], - op: function (context, on) { - var target = on ? on : context.me; - if (!(target instanceof EventTarget)) - throw new Error("Not a valid event target: " + this.on.sourceFor()); - return new Promise((resolve) => { - var resolved = false; - for (const eventInfo of events) { - var listener = (event) => { - context.result = event; - if (eventInfo.args) { - for (const arg of eventInfo.args) { - context.locals[arg.value] = - event[arg.value] || (event.detail ? event.detail[arg.value] : null); - } - } - if (!resolved) { - resolved = true; - resolve(runtime.findNext(this, context)); - } - }; - if (eventInfo.name){ - target.addEventListener(eventInfo.name, listener, {once: true}); - } else if (eventInfo.time != null) { - setTimeout(listener, eventInfo.time, eventInfo.time) - } - } - }); - }, - }; - return command; - } else { - var time; - if (tokens.matchToken("a")) { - tokens.requireToken("tick"); - time = 0; - } else { - time = parser.requireElement("expression", tokens); - } - - command = { - type: "waitCmd", - time: time, - args: [time], - op: function (context, timeValue) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(runtime.findNext(this, context)); - }, timeValue); - }); - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - }; - return command; - } - }); - - // TODO - colon path needs to eventually become part of ruby-style symbols - parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) { - var root = tokens.matchTokenType("IDENTIFIER"); - if (root) { - var path = [root.value]; - - var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":"); - if (separator) { - do { - path.push(tokens.requireTokenType("IDENTIFIER", "NUMBER").value); - } while (tokens.matchOpToken(separator.value)); - } - - return { - type: "dotOrColonPath", - path: path, - evaluate: function () { - return path.join(separator ? separator.value : ""); - }, - }; - } - }); - - - parser.addGrammarElement("eventName", function (parser, runtime, tokens) { - var token; - if ((token = tokens.matchTokenType("STRING"))) { - return { - evaluate: function() { - return token.value; - }, - }; - } - - return parser.parseElement("dotOrColonPath", tokens); - }); - - function parseSendCmd(cmdType, parser, runtime, tokens) { - var eventName = parser.requireElement("eventName", tokens); - - var details = parser.parseElement("namedArgumentList", tokens); - if ((cmdType === "send" && tokens.matchToken("to")) || - (cmdType === "trigger" && tokens.matchToken("on"))) { - var toExpr = parser.requireElement("expression", tokens); - } else { - var toExpr = parser.requireElement("implicitMeTarget", tokens); - } - - var sendCmd = { - eventName: eventName, - details: details, - to: toExpr, - args: [toExpr, eventName, details], - op: function (context, to, eventName, details) { - runtime.nullCheck(to, toExpr); - runtime.implicitLoop(to, function (target) { - runtime.triggerEvent(target, eventName, details, context.me); - }); - return runtime.findNext(sendCmd, context); - }, - }; - return sendCmd; - } - - parser.addCommand("trigger", function (parser, runtime, tokens) { - if (tokens.matchToken("trigger")) { - return parseSendCmd("trigger", parser, runtime, tokens); - } - }); - - parser.addCommand("send", function (parser, runtime, tokens) { - if (tokens.matchToken("send")) { - return parseSendCmd("send", parser, runtime, tokens); - } - }); - - var parseReturnFunction = function (parser, runtime, tokens, returnAValue) { - if (returnAValue) { - if (parser.commandBoundary(tokens.currentToken())) { - parser.raiseParseError(tokens, "'return' commands must return a value. If you do not wish to return a value, use 'exit' instead."); - } else { - var value = parser.requireElement("expression", tokens); - } - } - - var returnCmd = { - value: value, - args: [value], - op: function (context, value) { - var resolve = context.meta.resolve; - context.meta.returned = true; - context.meta.returnValue = value; - if (resolve) { - if (value) { - resolve(value); - } else { - resolve(); - } - } - return runtime.HALT; - }, - }; - return returnCmd; - }; - - parser.addCommand("return", function (parser, runtime, tokens) { - if (tokens.matchToken("return")) { - return parseReturnFunction(parser, runtime, tokens, true); - } - }); - - parser.addCommand("exit", function (parser, runtime, tokens) { - if (tokens.matchToken("exit")) { - return parseReturnFunction(parser, runtime, tokens, false); - } - }); - - parser.addCommand("halt", function (parser, runtime, tokens) { - if (tokens.matchToken("halt")) { - if (tokens.matchToken("the")) { - tokens.requireToken("event"); - // optional possessive - if (tokens.matchOpToken("'")) { - tokens.requireToken("s"); - } - var keepExecuting = true; - } - if (tokens.matchToken("bubbling")) { - var bubbling = true; - } else if (tokens.matchToken("default")) { - var haltDefault = true; - } - var exit = parseReturnFunction(parser, runtime, tokens, false); - - var haltCmd = { - keepExecuting: true, - bubbling: bubbling, - haltDefault: haltDefault, - exit: exit, - op: function (ctx) { - if (ctx.event) { - if (bubbling) { - ctx.event.stopPropagation(); - } else if (haltDefault) { - ctx.event.preventDefault(); - } else { - ctx.event.stopPropagation(); - ctx.event.preventDefault(); - } - if (keepExecuting) { - return runtime.findNext(this, ctx); - } else { - return exit; - } - } - }, - }; - return haltCmd; - } - }); - - parser.addCommand("log", function (parser, runtime, tokens) { - if (!tokens.matchToken("log")) return; - var exprs = [parser.parseElement("expression", tokens)]; - while (tokens.matchOpToken(",")) { - exprs.push(parser.requireElement("expression", tokens)); - } - if (tokens.matchToken("with")) { - var withExpr = parser.requireElement("expression", tokens); - } - var logCmd = { - exprs: exprs, - withExpr: withExpr, - args: [withExpr, exprs], - op: function (ctx, withExpr, values) { - if (withExpr) { - withExpr.apply(null, values); - } else { - console.log.apply(null, values); - } - return runtime.findNext(this, ctx); - }, - }; - return logCmd; - }); - - parser.addCommand("beep!", function (parser, runtime, tokens) { - if (!tokens.matchToken("beep!")) return; - var exprs = [parser.parseElement("expression", tokens)]; - while (tokens.matchOpToken(",")) { - exprs.push(parser.requireElement("expression", tokens)); - } - var beepCmd = { - exprs: exprs, - args: [exprs], - op: function (ctx, values) { - for (let i = 0; i < exprs.length; i++) { - const expr = exprs[i]; - const val = values[i]; - runtime.beepValueToConsole(ctx.me, expr, val); - } - return runtime.findNext(this, ctx); - }, - }; - return beepCmd; - }); - - parser.addCommand("throw", function (parser, runtime, tokens) { - if (!tokens.matchToken("throw")) return; - var expr = parser.requireElement("expression", tokens); - var throwCmd = { - expr: expr, - args: [expr], - op: function (ctx, expr) { - runtime.registerHyperTrace(ctx, expr); - throw expr; - }, - }; - return throwCmd; - }); - - var parseCallOrGet = function (parser, runtime, tokens) { - var expr = parser.requireElement("expression", tokens); - var callCmd = { - expr: expr, - args: [expr], - op: function (context, result) { - context.result = result; - return runtime.findNext(callCmd, context); - }, - }; - return callCmd; - }; - parser.addCommand("call", function (parser, runtime, tokens) { - if (!tokens.matchToken("call")) return; - var call = parseCallOrGet(parser, runtime, tokens); - if (call.expr && call.expr.type !== "functionCall") { - parser.raiseParseError(tokens, "Must be a function invocation"); - } - return call; - }); - parser.addCommand("get", function (parser, runtime, tokens) { - if (tokens.matchToken("get")) { - return parseCallOrGet(parser, runtime, tokens); - } - }); - - parser.addCommand("make", function (parser, runtime, tokens) { - if (!tokens.matchToken("make")) return; - tokens.matchToken("a") || tokens.matchToken("an"); - - var expr = parser.requireElement("expression", tokens); - - var args = []; - if (expr.type !== "queryRef" && tokens.matchToken("from")) { - do { - args.push(parser.requireElement("expression", tokens)); - } while (tokens.matchOpToken(",")); - } - - if (tokens.matchToken("called")) { - var target = parser.requireElement("symbol", tokens); - } - - var command; - if (expr.type === "queryRef") { - command = { - op: function (ctx) { - var match, - tagname = "div", - id, - classes = []; - var re = /(?:(^|#|\.)([^#\. ]+))/g; - while ((match = re.exec(expr.css))) { - if (match[1] === "") tagname = match[2].trim(); - else if (match[1] === "#") id = match[2].trim(); - else classes.push(match[2].trim()); - } - - var result = document.createElement(tagname); - if (id !== undefined) result.id = id; - for (var i = 0; i < classes.length; i++) { - var cls = classes[i]; - result.classList.add(cls) - } - - ctx.result = result; - if (target){ - runtime.setSymbol(target.name, ctx, target.scope, result); - } - - return runtime.findNext(this, ctx); - }, - }; - return command; - } else { - command = { - args: [expr, args], - op: function (ctx, expr, args) { - ctx.result = varargConstructor(expr, args); - if (target){ - runtime.setSymbol(target.name, ctx, target.scope, ctx.result); - } - - return runtime.findNext(this, ctx); - }, - }; - return command; - } - }); - - parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) { - - let lookAhead = tokens.token(1); - if (!(lookAhead && lookAhead.op && (lookAhead.value === '.' || lookAhead.value === "("))) { - return null; - } - - var expr = parser.requireElement("primaryExpression", tokens); - - var rootRoot = expr.root; - var root = expr; - while (rootRoot.root != null) { - root = root.root; - rootRoot = rootRoot.root; - } - - if (expr.type !== "functionCall") { - parser.raiseParseError(tokens, "Pseudo-commands must be function calls"); - } - - if (root.type === "functionCall" && root.root.root == null) { - if (tokens.matchAnyToken("the", "to", "on", "with", "into", "from", "at")) { - var realRoot = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("me")) { - var realRoot = parser.requireElement("implicitMeTarget", tokens); - } - } - - /** @type {ASTNode} */ - - var pseudoCommand - if(realRoot){ - pseudoCommand = { - type: "pseudoCommand", - root: realRoot, - argExressions: root.argExressions, - args: [realRoot, root.argExressions], - op: function (context, rootRoot, args) { - runtime.nullCheck(rootRoot, realRoot); - var func = rootRoot[root.root.name]; - runtime.nullCheck(func, root); - if (func.hyperfunc) { - args.push(context); - } - context.result = func.apply(rootRoot, args); - return runtime.findNext(pseudoCommand, context); - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - } - } else { - pseudoCommand = { - type: "pseudoCommand", - expr: expr, - args: [expr], - op: function (context, result) { - context.result = result; - return runtime.findNext(pseudoCommand, context); - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - }; - } - - return pseudoCommand; - }); - - /** - * @param {Parser} parser - * @param {Runtime} runtime - * @param {Tokens} tokens - * @param {*} target - * @param {*} value - * @returns - */ - var makeSetter = function (parser, runtime, tokens, target, value) { - - var symbolWrite = target.type === "symbol"; - var attributeWrite = target.type === "attributeRef"; - var styleWrite = target.type === "styleRef"; - var arrayWrite = target.type === "arrayIndex"; - - if (!(attributeWrite || styleWrite || symbolWrite) && target.root == null) { - parser.raiseParseError(tokens, "Can only put directly into symbols, not references"); - } - - var rootElt = null; - var prop = null; - if (symbolWrite) { - // rootElt is null - } else if (attributeWrite || styleWrite) { - rootElt = parser.requireElement("implicitMeTarget", tokens); - var attribute = target; - } else if(arrayWrite) { - prop = target.firstIndex; - rootElt = target.root; - } else { - prop = target.prop ? target.prop.value : null; - var attribute = target.attribute; - rootElt = target.root; - } - - /** @type {ASTNode} */ - var setCmd = { - target: target, - symbolWrite: symbolWrite, - value: value, - args: [rootElt, prop, value], - op: function (context, root, prop, valueToSet) { - if (symbolWrite) { - runtime.setSymbol(target.name, context, target.scope, valueToSet); - } else { - runtime.nullCheck(root, rootElt); - if (arrayWrite) { - root[prop] = valueToSet; - } else { - runtime.implicitLoop(root, function (elt) { - if (attribute) { - if (attribute.type === "attributeRef") { - if (valueToSet == null) { - elt.removeAttribute(attribute.name); - } else { - elt.setAttribute(attribute.name, valueToSet); - } - } else { - elt.style[attribute.name] = valueToSet; - } - } else { - elt[prop] = valueToSet; - } - }); - } - } - return runtime.findNext(this, context); - }, - }; - return setCmd; - }; - - parser.addCommand("default", function (parser, runtime, tokens) { - if (!tokens.matchToken("default")) return; - var target = parser.requireElement("assignableExpression", tokens); - tokens.requireToken("to"); - - var value = parser.requireElement("expression", tokens); - - /** @type {ASTNode} */ - var setter = makeSetter(parser, runtime, tokens, target, value); - var defaultCmd = { - target: target, - value: value, - setter: setter, - args: [target], - op: function (context, target) { - if (target) { - return runtime.findNext(this, context); - } else { - return setter; - } - }, - }; - setter.parent = defaultCmd; - return defaultCmd; - }); - - parser.addCommand("set", function (parser, runtime, tokens) { - if (!tokens.matchToken("set")) return; - if (tokens.currentToken().type === "L_BRACE") { - var obj = parser.requireElement("objectLiteral", tokens); - tokens.requireToken("on"); - var target = parser.requireElement("expression", tokens); - - var command = { - objectLiteral: obj, - target: target, - args: [obj, target], - op: function (ctx, obj, target) { - Object.assign(target, obj); - return runtime.findNext(this, ctx); - }, - }; - return command; - } - - try { - tokens.pushFollow("to"); - var target = parser.requireElement("assignableExpression", tokens); - } finally { - tokens.popFollow(); - } - tokens.requireToken("to"); - var value = parser.requireElement("expression", tokens); - return makeSetter(parser, runtime, tokens, target, value); - }); - - parser.addCommand("if", function (parser, runtime, tokens) { - if (!tokens.matchToken("if")) return; - var expr = parser.requireElement("expression", tokens); - tokens.matchToken("then"); // optional 'then' - var trueBranch = parser.parseElement("commandList", tokens); - var nestedIfStmt = false; - if (tokens.matchToken("else") || tokens.matchToken("otherwise")) { - nestedIfStmt = tokens.peekToken("if"); - var falseBranch = parser.parseElement("commandList", tokens); - } - if (tokens.hasMore() && !nestedIfStmt) { - tokens.requireToken("end"); - } - - /** @type {ASTNode} */ - var ifCmd = { - expr: expr, - trueBranch: trueBranch, - falseBranch: falseBranch, - args: [expr], - op: function (context, exprValue) { - if (exprValue) { - return trueBranch; - } else if (falseBranch) { - return falseBranch; - } else { - return runtime.findNext(this, context); - } - }, - }; - parser.setParent(trueBranch, ifCmd); - parser.setParent(falseBranch, ifCmd); - return ifCmd; - }); - - var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) { - var innerStartToken = tokens.currentToken(); - var identifier; - if (tokens.matchToken("for") || startedWithForToken) { - var identifierToken = tokens.requireTokenType("IDENTIFIER"); - identifier = identifierToken.value; - tokens.requireToken("in"); - var expression = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("in")) { - identifier = "it"; - var expression = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("while")) { - var whileExpr = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("until")) { - var isUntil = true; - if (tokens.matchToken("event")) { - var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name"); - if (tokens.matchToken("from")) { - var on = parser.requireElement("expression", tokens); - } - } else { - var whileExpr = parser.requireElement("expression", tokens); - } - } else { - if (!parser.commandBoundary(tokens.currentToken()) && - tokens.currentToken().value !== 'forever') { - var times = parser.requireElement("expression", tokens); - tokens.requireToken("times"); - } else { - tokens.matchToken("forever"); // consume optional forever - var forever = true; - } - } - - if (tokens.matchToken("index")) { - var identifierToken = tokens.requireTokenType("IDENTIFIER"); - var indexIdentifier = identifierToken.value; - } - - var loop = parser.parseElement("commandList", tokens); - if (loop && evt) { - // if this is an event based loop, wait a tick at the end of the loop so that - // events have a chance to trigger in the loop condition o_O))) - var last = loop; - while (last.next) { - last = last.next; - } - var waitATick = { - type: "waitATick", - op: function () { - return new Promise(function (resolve) { - setTimeout(function () { - resolve(runtime.findNext(waitATick)); - }, 0); - }); - }, - }; - last.next = waitATick; - } - if (tokens.hasMore()) { - tokens.requireToken("end"); - } - - if (identifier == null) { - identifier = "_implicit_repeat_" + innerStartToken.start; - var slot = identifier; - } else { - var slot = identifier + "_" + innerStartToken.start; - } - - var repeatCmd = { - identifier: identifier, - indexIdentifier: indexIdentifier, - slot: slot, - expression: expression, - forever: forever, - times: times, - until: isUntil, - event: evt, - on: on, - whileExpr: whileExpr, - resolveNext: function () { - return this; - }, - loop: loop, - args: [whileExpr, times], - op: function (context, whileValue, times) { - var iteratorInfo = context.meta.iterators[slot]; - var keepLooping = false; - var loopVal = null; - if (this.forever) { - keepLooping = true; - } else if (this.until) { - if (evt) { - keepLooping = context.meta.iterators[slot].eventFired === false; - } else { - keepLooping = whileValue !== true; - } - } else if (whileExpr) { - keepLooping = whileValue; - } else if (times) { - keepLooping = iteratorInfo.index < times; - } else { - var nextValFromIterator = iteratorInfo.iterator.next(); - keepLooping = !nextValFromIterator.done; - loopVal = nextValFromIterator.value; - } - - if (keepLooping) { - if (iteratorInfo.value) { - context.result = context.locals[identifier] = loopVal; - } else { - context.result = iteratorInfo.index; - } - if (indexIdentifier) { - context.locals[indexIdentifier] = iteratorInfo.index; - } - iteratorInfo.index++; - return loop; - } else { - context.meta.iterators[slot] = null; - return runtime.findNext(this.parent, context); - } - }, - }; - parser.setParent(loop, repeatCmd); - var repeatInit = { - name: "repeatInit", - args: [expression, evt, on], - op: function (context, value, event, on) { - var iteratorInfo = { - index: 0, - value: value, - eventFired: false, - }; - context.meta.iterators[slot] = iteratorInfo; - if (value && value[Symbol.iterator]) { - iteratorInfo.iterator = value[Symbol.iterator](); - } - if (evt) { - var target = on || context.me; - target.addEventListener( - event, - function (e) { - context.meta.iterators[slot].eventFired = true; - }, - { once: true } - ); - } - return repeatCmd; // continue to loop - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - }; - parser.setParent(repeatCmd, repeatInit); - return repeatInit; - }; - - parser.addCommand("repeat", function (parser, runtime, tokens) { - if (tokens.matchToken("repeat")) { - return parseRepeatExpression(parser, tokens, runtime, false); - } - }); - - parser.addCommand("for", function (parser, runtime, tokens) { - if (tokens.matchToken("for")) { - return parseRepeatExpression(parser, tokens, runtime, true); - } - }); - - parser.addCommand("continue", function (parser, runtime, tokens) { - - if (!tokens.matchToken("continue")) return; - - var command = { - op: function (context) { - - // scan for the closest repeat statement - for (var parent = this.parent ; true ; parent = parent.parent) { - - if (parent == undefined) { - parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.") - } - if (parent.loop != undefined) { - return parent.resolveNext(context) - } - } - } - }; - return command; - }); - - parser.addCommand("break", function (parser, runtime, tokens) { - - if (!tokens.matchToken("break")) return; - - var command = { - op: function (context) { - - // scan for the closest repeat statement - for (var parent = this.parent ; true ; parent = parent.parent) { - - if (parent == undefined) { - parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.") - } - if (parent.loop != undefined) { - return runtime.findNext(parent.parent, context); - } - } - } - }; - return command; - }); - - parser.addGrammarElement("stringLike", function (parser, runtime, tokens) { - return parser.parseAnyOf(["string", "nakedString"], tokens); - }); - - parser.addCommand("append", function (parser, runtime, tokens) { - if (!tokens.matchToken("append")) return; - var targetExpr = null; - - var value = parser.requireElement("expression", tokens); - - /** @type {ASTNode} */ - var implicitResultSymbol = { - type: "symbol", - evaluate: function (context) { - return runtime.resolveSymbol("result", context); - }, - }; - - if (tokens.matchToken("to")) { - targetExpr = parser.requireElement("expression", tokens); - } else { - targetExpr = implicitResultSymbol; - } - - var setter = null; - if (targetExpr.type === "symbol" || targetExpr.type === "attributeRef" || targetExpr.root != null) { - setter = makeSetter(parser, runtime, tokens, targetExpr, implicitResultSymbol); - } - - var command = { - value: value, - target: targetExpr, - args: [targetExpr, value], - op: function (context, target, value) { - if (Array.isArray(target)) { - target.push(value); - return runtime.findNext(this, context); - } else if (target instanceof Element) { - target.innerHTML += value; - return runtime.findNext(this, context); - } else if(setter) { - context.result = (target || "") + value; - return setter; - } else { - throw Error("Unable to append a value!") - } - }, - execute: function (context) { - return runtime.unifiedExec(this, context/*, value, target*/); - }, - }; - - if (setter != null) { - setter.parent = command; - } - - return command; - }); - - function parsePickRange(parser, runtime, tokens) { - tokens.matchToken("at") || tokens.matchToken("from"); - const rv = { includeStart: true, includeEnd: false } - - rv.from = tokens.matchToken("start") ? 0 : parser.requireElement("expression", tokens) - - if (tokens.matchToken("to") || tokens.matchOpToken("..")) { - if (tokens.matchToken("end")) { - rv.toEnd = true; - } else { - rv.to = parser.requireElement("expression", tokens); - } - } - - if (tokens.matchToken("inclusive")) rv.includeEnd = true; - else if (tokens.matchToken("exclusive")) rv.includeStart = false; - - return rv; - } - - class RegExpIterator { - constructor(re, str) { - this.re = re; - this.str = str; - } - - next() { - const match = this.re.exec(this.str); - if (match === null) return { done: true }; - else return { value: match }; - } - } - - class RegExpIterable { - constructor(re, flags, str) { - this.re = re; - this.flags = flags; - this.str = str; - } - - [Symbol.iterator]() { - return new RegExpIterator(new RegExp(this.re, this.flags), this.str); - } - } - - parser.addCommand("pick", (parser, runtime, tokens) => { - if (!tokens.matchToken("pick")) return; - - tokens.matchToken("the"); - - if (tokens.matchToken("item") || tokens.matchToken("items") - || tokens.matchToken("character") || tokens.matchToken("characters")) { - const range = parsePickRange(parser, runtime, tokens); - - tokens.requireToken("from"); - const root = parser.requireElement("expression", tokens); - - return { - args: [root, range.from, range.to], - op(ctx, root, from, to) { - if (range.toEnd) to = root.length; - if (!range.includeStart) from++; - if (range.includeEnd) to++; - if (to == null || to == undefined) to = from + 1; - ctx.result = root.slice(from, to); - return runtime.findNext(this, ctx); - } - } - } - - if (tokens.matchToken("match")) { - tokens.matchToken("of"); - const re = parser.parseElement("expression", tokens); - let flags = "" - if (tokens.matchOpToken("|")) { - flags = tokens.requireToken("identifier").value; - } - - tokens.requireToken("from"); - const root = parser.parseElement("expression", tokens); - - return { - args: [root, re], - op(ctx, root, re) { - ctx.result = new RegExp(re, flags).exec(root); - return runtime.findNext(this, ctx); - } - } - } - - if (tokens.matchToken("matches")) { - tokens.matchToken("of"); - const re = parser.parseElement("expression", tokens); - let flags = "gu" - if (tokens.matchOpToken("|")) { - flags = 'g' + tokens.requireToken("identifier").value.replace('g', ''); - } - console.log('flags', flags) - - tokens.requireToken("from"); - const root = parser.parseElement("expression", tokens); - - return { - args: [root, re], - op(ctx, root, re) { - ctx.result = new RegExpIterable(re, flags, root); - return runtime.findNext(this, ctx); - } - } - } - }); - - parser.addCommand("increment", function (parser, runtime, tokens) { - if (!tokens.matchToken("increment")) return; - var amountExpr; - - // This is optional. Defaults to "result" - var target = parser.parseElement("assignableExpression", tokens); - - // This is optional. Defaults to 1. - if (tokens.matchToken("by")) { - amountExpr = parser.requireElement("expression", tokens); - } - - var implicitIncrementOp = { - type: "implicitIncrementOp", - target: target, - args: [target, amountExpr], - op: function (context, targetValue, amount) { - targetValue = targetValue ? parseFloat(targetValue) : 0; - amount = amountExpr ? parseFloat(amount) : 1; - var newValue = targetValue + amount; - context.result = newValue; - return newValue; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - } - }; - - return makeSetter(parser, runtime, tokens, target, implicitIncrementOp); - }); - - parser.addCommand("decrement", function (parser, runtime, tokens) { - if (!tokens.matchToken("decrement")) return; - var amountExpr; - - // This is optional. Defaults to "result" - var target = parser.parseElement("assignableExpression", tokens); - - // This is optional. Defaults to 1. - if (tokens.matchToken("by")) { - amountExpr = parser.requireElement("expression", tokens); - } - - var implicitDecrementOp = { - type: "implicitDecrementOp", - target: target, - args: [target, amountExpr], - op: function (context, targetValue, amount) { - targetValue = targetValue ? parseFloat(targetValue) : 0; - amount = amountExpr ? parseFloat(amount) : 1; - var newValue = targetValue - amount; - context.result = newValue; - return newValue; - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - } - }; - - return makeSetter(parser, runtime, tokens, target, implicitDecrementOp); - }); - - function parseConversionInfo(tokens, parser) { - var type = "text"; - var conversion; - tokens.matchToken("a") || tokens.matchToken("an"); - if (tokens.matchToken("json") || tokens.matchToken("Object")) { - type = "json"; - } else if (tokens.matchToken("response")) { - type = "response"; - } else if (tokens.matchToken("html")) { - type = "html"; - } else if (tokens.matchToken("text")) { - // default, ignore - } else { - conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); - } - return {type, conversion}; - } - - parser.addCommand("fetch", function (parser, runtime, tokens) { - if (!tokens.matchToken("fetch")) return; - var url = parser.requireElement("stringLike", tokens); - - if (tokens.matchToken("as")) { - var conversionInfo = parseConversionInfo(tokens, parser); - } - - if (tokens.matchToken("with") && tokens.currentToken().value !== "{") { - var args = parser.parseElement("nakedNamedArgumentList", tokens); - } else { - var args = parser.parseElement("objectLiteral", tokens); - } - - if (conversionInfo == null && tokens.matchToken("as")) { - conversionInfo = parseConversionInfo(tokens, parser); - } - - var type = conversionInfo ? conversionInfo.type : "text"; - var conversion = conversionInfo ? conversionInfo.conversion : null - - /** @type {ASTNode} */ - var fetchCmd = { - url: url, - argExpressions: args, - args: [url, args], - op: function (context, url, args) { - var detail = args || {}; - detail["sender"] = context.me; - detail["headers"] = detail["headers"] || {} - var abortController = new AbortController(); - let abortListener = context.me.addEventListener('fetch:abort', function(){ - abortController.abort(); - }, {once: true}); - detail['signal'] = abortController.signal; - runtime.triggerEvent(context.me, "hyperscript:beforeFetch", detail); - runtime.triggerEvent(context.me, "fetch:beforeRequest", detail); - args = detail; - var finished = false; - if (args.timeout) { - setTimeout(function () { - if (!finished) { - abortController.abort(); - } - }, args.timeout); - } - return fetch(url, args) - .then(function (resp) { - let resultDetails = {response:resp}; - runtime.triggerEvent(context.me, "fetch:afterResponse", resultDetails); - resp = resultDetails.response; - - if (type === "response") { - context.result = resp; - runtime.triggerEvent(context.me, "fetch:afterRequest", {result:resp}); - finished = true; - return runtime.findNext(fetchCmd, context); - } - if (type === "json") { - return resp.json().then(function (result) { - context.result = result; - runtime.triggerEvent(context.me, "fetch:afterRequest", {result}); - finished = true; - return runtime.findNext(fetchCmd, context); - }); - } - return resp.text().then(function (result) { - if (conversion) result = runtime.convertValue(result, conversion); - - if (type === "html") result = runtime.convertValue(result, "Fragment"); - - context.result = result; - runtime.triggerEvent(context.me, "fetch:afterRequest", {result}); - finished = true; - return runtime.findNext(fetchCmd, context); - }); - }) - .catch(function (reason) { - runtime.triggerEvent(context.me, "fetch:error", { - reason: reason, - }); - throw reason; - }).finally(function(){ - context.me.removeEventListener('fetch:abort', abortListener); - }); - }, - }; - return fetchCmd; - }); - } - - function hyperscriptWebGrammar(parser) { - parser.addCommand("settle", function (parser, runtime, tokens) { - if (tokens.matchToken("settle")) { - if (!parser.commandBoundary(tokens.currentToken())) { - var onExpr = parser.requireElement("expression", tokens); - } else { - var onExpr = parser.requireElement("implicitMeTarget", tokens); - } - - var settleCommand = { - type: "settleCmd", - args: [onExpr], - op: function (context, on) { - runtime.nullCheck(on, onExpr); - var resolve = null; - var resolved = false; - var transitionStarted = false; - - var promise = new Promise(function (r) { - resolve = r; - }); - - // listen for a transition begin - on.addEventListener( - "transitionstart", - function () { - transitionStarted = true; - }, - { once: true } - ); - - // if no transition begins in 500ms, cancel - setTimeout(function () { - if (!transitionStarted && !resolved) { - resolve(runtime.findNext(settleCommand, context)); - } - }, 500); - - // continue on a transition emd - on.addEventListener( - "transitionend", - function () { - if (!resolved) { - resolve(runtime.findNext(settleCommand, context)); - } - }, - { once: true } - ); - return promise; - }, - execute: function (context) { - return runtime.unifiedExec(this, context); - }, - }; - return settleCommand; - } - }); - - parser.addCommand("add", function (parser, runtime, tokens) { - if (tokens.matchToken("add")) { - var classRef = parser.parseElement("classRef", tokens); - var attributeRef = null; - var cssDeclaration = null; - if (classRef == null) { - attributeRef = parser.parseElement("attributeRef", tokens); - if (attributeRef == null) { - cssDeclaration = parser.parseElement("styleLiteral", tokens); - if (cssDeclaration == null) { - parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); - } - } - } else { - var classRefs = [classRef]; - while ((classRef = parser.parseElement("classRef", tokens))) { - classRefs.push(classRef); - } - } - - if (tokens.matchToken("to")) { - var toExpr = parser.requireElement("expression", tokens); - } else { - var toExpr = parser.requireElement("implicitMeTarget", tokens); - } - - if (tokens.matchToken("when")) { - if (cssDeclaration) { - parser.raiseParseError(tokens, "Only class and properties are supported with a when clause") - } - var when = parser.requireElement("expression", tokens); - } - - if (classRefs) { - return { - classRefs: classRefs, - to: toExpr, - args: [toExpr, classRefs], - op: function (context, to, classRefs) { - runtime.nullCheck(to, toExpr); - runtime.forEach(classRefs, function (classRef) { - runtime.implicitLoop(to, function (target) { - if (when) { - context.result = target; - let whenResult = runtime.evaluateNoPromise(when, context); - if (whenResult) { - if (target instanceof Element) target.classList.add(classRef.className); - } else { - if (target instanceof Element) target.classList.remove(classRef.className); - } - context.result = null; - } else { - if (target instanceof Element) target.classList.add(classRef.className); - } - }); - }); - return runtime.findNext(this, context); - }, - }; - } else if (attributeRef) { - return { - type: "addCmd", - attributeRef: attributeRef, - to: toExpr, - args: [toExpr], - op: function (context, to, attrRef) { - runtime.nullCheck(to, toExpr); - runtime.implicitLoop(to, function (target) { - if (when) { - context.result = target; - let whenResult = runtime.evaluateNoPromise(when, context); - if (whenResult) { - target.setAttribute(attributeRef.name, attributeRef.value); - } else { - target.removeAttribute(attributeRef.name); - } - context.result = null; - } else { - target.setAttribute(attributeRef.name, attributeRef.value); - } - }); - return runtime.findNext(this, context); - }, - execute: function (ctx) { - return runtime.unifiedExec(this, ctx); - }, - }; - } else { - return { - type: "addCmd", - cssDeclaration: cssDeclaration, - to: toExpr, - args: [toExpr, cssDeclaration], - op: function (context, to, css) { - runtime.nullCheck(to, toExpr); - runtime.implicitLoop(to, function (target) { - target.style.cssText += css; - }); - return runtime.findNext(this, context); - }, - execute: function (ctx) { - return runtime.unifiedExec(this, ctx); - }, - }; - } - } - }); - - parser.addGrammarElement("styleLiteral", function (parser, runtime, tokens) { - if (!tokens.matchOpToken("{")) return; - - var stringParts = [""] - var exprs = [] - - while (tokens.hasMore()) { - if (tokens.matchOpToken("\\")) { - tokens.consumeToken(); - } else if (tokens.matchOpToken("}")) { - break; - } else if (tokens.matchToken("$")) { - var opencurly = tokens.matchOpToken("{"); - var expr = parser.parseElement("expression", tokens); - if (opencurly) tokens.requireOpToken("}"); - - exprs.push(expr) - stringParts.push("") - } else { - var tok = tokens.consumeToken(); - stringParts[stringParts.length-1] += tokens.source.substring(tok.start, tok.end); - } - - stringParts[stringParts.length-1] += tokens.lastWhitespace(); - } - - return { - type: "styleLiteral", - args: [exprs], - op: function (ctx, exprs) { - var rv = ""; - - stringParts.forEach(function (part, idx) { - rv += part; - if (idx in exprs) rv += exprs[idx]; - }); - - return rv; - }, - evaluate: function(ctx) { - return runtime.unifiedEval(this, ctx); - } - } - }) - - parser.addCommand("remove", function (parser, runtime, tokens) { - if (tokens.matchToken("remove")) { - var classRef = parser.parseElement("classRef", tokens); - var attributeRef = null; - var elementExpr = null; - if (classRef == null) { - attributeRef = parser.parseElement("attributeRef", tokens); - if (attributeRef == null) { - elementExpr = parser.parseElement("expression", tokens); - if (elementExpr == null) { - parser.raiseParseError( - tokens, - "Expected either a class reference, attribute expression or value expression" - ); - } - } - } else { - var classRefs = [classRef]; - while ((classRef = parser.parseElement("classRef", tokens))) { - classRefs.push(classRef); - } - } - - if (tokens.matchToken("from")) { - var fromExpr = parser.requireElement("expression", tokens); - } else { - if (elementExpr == null) { - var fromExpr = parser.requireElement("implicitMeTarget", tokens); - } - } - - if (elementExpr) { - return { - elementExpr: elementExpr, - from: fromExpr, - args: [elementExpr, fromExpr], - op: function (context, element, from) { - runtime.nullCheck(element, elementExpr); - runtime.implicitLoop(element, function (target) { - if (target.parentElement && (from == null || from.contains(target))) { - target.parentElement.removeChild(target); - } - }); - return runtime.findNext(this, context); - }, - }; - } else { - return { - classRefs: classRefs, - attributeRef: attributeRef, - elementExpr: elementExpr, - from: fromExpr, - args: [classRefs, fromExpr], - op: function (context, classRefs, from) { - runtime.nullCheck(from, fromExpr); - if (classRefs) { - runtime.forEach(classRefs, function (classRef) { - runtime.implicitLoop(from, function (target) { - target.classList.remove(classRef.className); - }); - }); - } else { - runtime.implicitLoop(from, function (target) { - target.removeAttribute(attributeRef.name); - }); - } - return runtime.findNext(this, context); - }, - }; - } - } - }); - - parser.addCommand("toggle", function (parser, runtime, tokens) { - if (tokens.matchToken("toggle")) { - tokens.matchAnyToken("the", "my"); - if (tokens.currentToken().type === "STYLE_REF") { - let styleRef = tokens.consumeToken(); - var name = styleRef.value.substr(1); - var visibility = true; - var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); - if (tokens.matchToken("of")) { - tokens.pushFollow("with"); - try { - var onExpr = parser.requireElement("expression", tokens); - } finally { - tokens.popFollow(); - } - } else { - var onExpr = parser.requireElement("implicitMeTarget", tokens); - } - } else if (tokens.matchToken("between")) { - var between = true; - var classRef = parser.parseElement("classRef", tokens); - tokens.requireToken("and"); - var classRef2 = parser.requireElement("classRef", tokens); - } else { - var classRef = parser.parseElement("classRef", tokens); - var attributeRef = null; - if (classRef == null) { - attributeRef = parser.parseElement("attributeRef", tokens); - if (attributeRef == null) { - parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); - } - } else { - var classRefs = [classRef]; - while ((classRef = parser.parseElement("classRef", tokens))) { - classRefs.push(classRef); - } - } - } - - if (visibility !== true) { - if (tokens.matchToken("on")) { - var onExpr = parser.requireElement("expression", tokens); - } else { - var onExpr = parser.requireElement("implicitMeTarget", tokens); - } - } - - if (tokens.matchToken("for")) { - var time = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("until")) { - var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name"); - if (tokens.matchToken("from")) { - var from = parser.requireElement("expression", tokens); - } - } - - var toggleCmd = { - classRef: classRef, - classRef2: classRef2, - classRefs: classRefs, - attributeRef: attributeRef, - on: onExpr, - time: time, - evt: evt, - from: from, - toggle: function (on, classRef, classRef2, classRefs) { - runtime.nullCheck(on, onExpr); - if (visibility) { - runtime.implicitLoop(on, function (target) { - hideShowStrategy("toggle", target); - }); - } else if (between) { - runtime.implicitLoop(on, function (target) { - if (target.classList.contains(classRef.className)) { - target.classList.remove(classRef.className); - target.classList.add(classRef2.className); - } else { - target.classList.add(classRef.className); - target.classList.remove(classRef2.className); - } - }); - } else if (classRefs) { - runtime.forEach(classRefs, function (classRef) { - runtime.implicitLoop(on, function (target) { - target.classList.toggle(classRef.className); - }); - }); - } else { - runtime.forEach(on, function (target) { - if (target.hasAttribute(attributeRef.name)) { - target.removeAttribute(attributeRef.name); - } else { - target.setAttribute(attributeRef.name, attributeRef.value); - } - }); - } - }, - args: [onExpr, time, evt, from, classRef, classRef2, classRefs], - op: function (context, on, time, evt, from, classRef, classRef2, classRefs) { - if (time) { - return new Promise(function (resolve) { - toggleCmd.toggle(on, classRef, classRef2, classRefs); - setTimeout(function () { - toggleCmd.toggle(on, classRef, classRef2, classRefs); - resolve(runtime.findNext(toggleCmd, context)); - }, time); - }); - } else if (evt) { - return new Promise(function (resolve) { - var target = from || context.me; - target.addEventListener( - evt, - function () { - toggleCmd.toggle(on, classRef, classRef2, classRefs); - resolve(runtime.findNext(toggleCmd, context)); - }, - { once: true } - ); - toggleCmd.toggle(on, classRef, classRef2, classRefs); - }); - } else { - this.toggle(on, classRef, classRef2, classRefs); - return runtime.findNext(toggleCmd, context); - } - }, - }; - return toggleCmd; - } - }); - - var HIDE_SHOW_STRATEGIES = { - display: function (op, element, arg) { - if (arg) { - element.style.display = arg; - } else if (op === "toggle") { - if (getComputedStyle(element).display === "none") { - HIDE_SHOW_STRATEGIES.display("show", element, arg); - } else { - HIDE_SHOW_STRATEGIES.display("hide", element, arg); - } - } else if (op === "hide") { - const internalData = parser.runtime.getInternalData(element); - if (internalData.originalDisplay == null) { - internalData.originalDisplay = element.style.display; - } - element.style.display = "none"; - } else { - const internalData = parser.runtime.getInternalData(element); - if (internalData.originalDisplay && internalData.originalDisplay !== 'none') { - element.style.display = internalData.originalDisplay; - } else { - element.style.removeProperty('display'); - } - } - }, - visibility: function (op, element, arg) { - if (arg) { - element.style.visibility = arg; - } else if (op === "toggle") { - if (getComputedStyle(element).visibility === "hidden") { - HIDE_SHOW_STRATEGIES.visibility("show", element, arg); - } else { - HIDE_SHOW_STRATEGIES.visibility("hide", element, arg); - } - } else if (op === "hide") { - element.style.visibility = "hidden"; - } else { - element.style.visibility = "visible"; - } - }, - opacity: function (op, element, arg) { - if (arg) { - element.style.opacity = arg; - } else if (op === "toggle") { - if (getComputedStyle(element).opacity === "0") { - HIDE_SHOW_STRATEGIES.opacity("show", element, arg); - } else { - HIDE_SHOW_STRATEGIES.opacity("hide", element, arg); - } - } else if (op === "hide") { - element.style.opacity = "0"; - } else { - element.style.opacity = "1"; - } - }, - }; - - var parseShowHideTarget = function (parser, runtime, tokens) { - var target; - var currentTokenValue = tokens.currentToken(); - if (currentTokenValue.value === "when" || currentTokenValue.value === "with" || parser.commandBoundary(currentTokenValue)) { - target = parser.parseElement("implicitMeTarget", tokens); - } else { - target = parser.parseElement("expression", tokens); - } - return target; - }; - - var resolveHideShowStrategy = function (parser, tokens, name) { - var configDefault = config.defaultHideShowStrategy; - var strategies = HIDE_SHOW_STRATEGIES; - if (config.hideShowStrategies) { - strategies = Object.assign(strategies, config.hideShowStrategies); // merge in user provided strategies - } - name = name || configDefault || "display"; - var value = strategies[name]; - if (value == null) { - parser.raiseParseError(tokens, "Unknown show/hide strategy : " + name); - } - return value; - }; - - parser.addCommand("hide", function (parser, runtime, tokens) { - if (tokens.matchToken("hide")) { - var targetExpr = parseShowHideTarget(parser, runtime, tokens); - - var name = null; - if (tokens.matchToken("with")) { - name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value; - if (name.indexOf("*") === 0) { - name = name.substr(1); - } - } - var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); - - return { - target: targetExpr, - args: [targetExpr], - op: function (ctx, target) { - runtime.nullCheck(target, targetExpr); - runtime.implicitLoop(target, function (elt) { - hideShowStrategy("hide", elt); - }); - return runtime.findNext(this, ctx); - }, - }; - } - }); - - parser.addCommand("show", function (parser, runtime, tokens) { - if (tokens.matchToken("show")) { - var targetExpr = parseShowHideTarget(parser, runtime, tokens); - - var name = null; - if (tokens.matchToken("with")) { - name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value; - if (name.indexOf("*") === 0) { - name = name.substr(1); - } - } - var arg = null; - if (tokens.matchOpToken(":")) { - var tokenArr = tokens.consumeUntilWhitespace(); - tokens.matchTokenType("WHITESPACE"); - arg = tokenArr - .map(function (t) { - return t.value; - }) - .join(""); - } - - if (tokens.matchToken("when")) { - var when = parser.requireElement("expression", tokens); - } - - var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name); - - return { - target: targetExpr, - when: when, - args: [targetExpr], - op: function (ctx, target) { - runtime.nullCheck(target, targetExpr); - runtime.implicitLoop(target, function (elt) { - if (when) { - ctx.result = elt; - let whenResult = runtime.evaluateNoPromise(when, ctx); - if (whenResult) { - hideShowStrategy("show", elt, arg); - } else { - hideShowStrategy("hide", elt); - } - ctx.result = null; - } else { - hideShowStrategy("show", elt, arg); - } - }); - return runtime.findNext(this, ctx); - }, - }; - } - }); - - parser.addCommand("take", function (parser, runtime, tokens) { - if (tokens.matchToken("take")) { - var classRef = parser.parseElement("classRef", tokens); - - var attributeRef = null; - var replacementValue = null; - - if (classRef == null) { - attributeRef = parser.parseElement("attributeRef", tokens); - if (attributeRef == null) { - parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); - } - - if (tokens.matchToken("with")) { - replacementValue = parser.requireElement("expression", tokens); - } - } - - if (tokens.matchToken("from")) { - var fromExpr = parser.requireElement("expression", tokens); - } else { - var fromExpr = classRef; - } - - if (tokens.matchToken("for")) { - var forExpr = parser.requireElement("expression", tokens); - } else { - var forExpr = parser.requireElement("implicitMeTarget", tokens); - } - - if (classRef) { - var takeCmd = { - classRef: classRef, - from: fromExpr, - forElt: forExpr, - args: [classRef, fromExpr, forExpr], - op: function (context, eltColl, from, forElt) { - runtime.nullCheck(from, fromExpr); - runtime.nullCheck(forElt, forExpr); - var clazz = eltColl.className; - runtime.implicitLoop(from, function (target) { - target.classList.remove(clazz); - }); - runtime.implicitLoop(forElt, function (target) { - target.classList.add(clazz); - }); - return runtime.findNext(this, context); - }, - }; - return takeCmd; - } else { - var takeCmd = { - attributeRef: attributeRef, - from: fromExpr, - forElt: forExpr, - args: [fromExpr, forExpr, replacementValue], - op: function (context, from, forElt, replacementValue) { - runtime.nullCheck(from, fromExpr); - runtime.nullCheck(forElt, forExpr); - runtime.implicitLoop(from, function (target) { - if (!replacementValue) { - target.removeAttribute(attributeRef.name); - } else { - target.setAttribute(attributeRef.name, replacementValue) - } - }); - runtime.implicitLoop(forElt, function (target) { - target.setAttribute(attributeRef.name, attributeRef.value || "") - }); - return runtime.findNext(this, context); - }, - }; - return takeCmd; - } - } - }); - - function putInto(runtime, context, prop, valueToPut) { - if (prop != null) { - var value = runtime.resolveSymbol(prop, context); - } else { - var value = context; - } - if (value instanceof Element || value instanceof HTMLDocument) { - while (value.firstChild) value.removeChild(value.firstChild); - value.append(parser.runtime.convertValue(valueToPut, "Fragment")); - runtime.processNode(value); - } else { - if (prop != null) { - runtime.setSymbol(prop, context, null, valueToPut); - } else { - throw "Don't know how to put a value into " + typeof context; - } - } - } - - parser.addCommand("put", function (parser, runtime, tokens) { - if (tokens.matchToken("put")) { - var value = parser.requireElement("expression", tokens); - - var operationToken = tokens.matchAnyToken("into", "before", "after"); - - if (operationToken == null && tokens.matchToken("at")) { - tokens.matchToken("the"); // optional "the" - operationToken = tokens.matchAnyToken("start", "end"); - tokens.requireToken("of"); - } - - if (operationToken == null) { - parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'at start of', 'at end of', 'after'"); - } - var target = parser.requireElement("expression", tokens); - - var operation = operationToken.value; - - var arrayIndex = false; - var symbolWrite = false; - var rootExpr = null; - var prop = null; - - if (target.type === "arrayIndex" && operation === "into") { - arrayIndex = true; - prop = target.prop; - rootExpr = target.root; - } else if (target.prop && target.root && operation === "into") { - prop = target.prop.value; - rootExpr = target.root; - } else if (target.type === "symbol" && operation === "into") { - symbolWrite = true; - prop = target.name; - } else if (target.type === "attributeRef" && operation === "into") { - var attributeWrite = true; - prop = target.name; - rootExpr = parser.requireElement("implicitMeTarget", tokens); - } else if (target.type === "styleRef" && operation === "into") { - var styleWrite = true; - prop = target.name; - rootExpr = parser.requireElement("implicitMeTarget", tokens); - } else if (target.attribute && operation === "into") { - var attributeWrite = target.attribute.type === "attributeRef"; - var styleWrite = target.attribute.type === "styleRef"; - prop = target.attribute.name; - rootExpr = target.root; - } else { - rootExpr = target; - } - - var putCmd = { - target: target, - operation: operation, - symbolWrite: symbolWrite, - value: value, - args: [rootExpr, prop, value], - op: function (context, root, prop, valueToPut) { - if (symbolWrite) { - putInto(runtime, context, prop, valueToPut); - } else { - runtime.nullCheck(root, rootExpr); - if (operation === "into") { - if (attributeWrite) { - runtime.implicitLoop(root, function (elt) { - elt.setAttribute(prop, valueToPut); - }); - } else if (styleWrite) { - runtime.implicitLoop(root, function (elt) { - elt.style[prop] = valueToPut; - }); - } else if (arrayIndex) { - root[prop] = valueToPut; - } else { - runtime.implicitLoop(root, function (elt) { - putInto(runtime, elt, prop, valueToPut); - }); - } - } else { - var op = - operation === "before" - ? Element.prototype.before - : operation === "after" - ? Element.prototype.after - : operation === "start" - ? Element.prototype.prepend - : operation === "end" - ? Element.prototype.append - : Element.prototype.append; // unreachable - - runtime.implicitLoop(root, function (elt) { - op.call( - elt, - valueToPut instanceof Node - ? valueToPut - : runtime.convertValue(valueToPut, "Fragment") - ); - // process any new content - if (elt.parentElement) { - runtime.processNode(elt.parentElement); - } else { - runtime.processNode(elt); - } - }); - } - } - return runtime.findNext(this, context); - }, - }; - return putCmd; - } - }); - - function parsePseudopossessiveTarget(parser, runtime, tokens) { - var targets; - if ( - tokens.matchToken("the") || - tokens.matchToken("element") || - tokens.matchToken("elements") || - tokens.currentToken().type === "CLASS_REF" || - tokens.currentToken().type === "ID_REF" || - (tokens.currentToken().op && tokens.currentToken().value === "<") - ) { - parser.possessivesDisabled = true; - try { - targets = parser.parseElement("expression", tokens); - } finally { - delete parser.possessivesDisabled; - } - // optional possessive - if (tokens.matchOpToken("'")) { - tokens.requireToken("s"); - } - } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.currentToken().value === "its") { - var identifier = tokens.matchToken("its"); - targets = { - type: "pseudopossessiveIts", - token: identifier, - name: identifier.value, - evaluate: function (context) { - return runtime.resolveSymbol("it", context); - }, - }; - } else { - tokens.matchToken("my") || tokens.matchToken("me"); // consume optional 'my' - targets = parser.parseElement("implicitMeTarget", tokens); - } - return targets; - } - - parser.addCommand("transition", function (parser, runtime, tokens) { - if (tokens.matchToken("transition")) { - var targetsExpr = parsePseudopossessiveTarget(parser, runtime, tokens); - - var properties = []; - var from = []; - var to = []; - var currentToken = tokens.currentToken(); - while ( - !parser.commandBoundary(currentToken) && - currentToken.value !== "over" && - currentToken.value !== "using" - ) { - if (tokens.currentToken().type === "STYLE_REF") { - let styleRef = tokens.consumeToken(); - let styleProp = styleRef.value.substr(1); - properties.push({ - type: "styleRefValue", - evaluate: function () { - return styleProp; - }, - }); - } else { - properties.push(parser.requireElement("stringLike", tokens)); - } - - if (tokens.matchToken("from")) { - from.push(parser.requireElement("expression", tokens)); - } else { - from.push(null); - } - tokens.requireToken("to"); - if (tokens.matchToken("initial")) { - to.push({ - type: "initial_literal", - evaluate : function(){ - return "initial"; - } - }); - } else { - to.push(parser.requireElement("expression", tokens)); - } - currentToken = tokens.currentToken(); - } - if (tokens.matchToken("over")) { - var over = parser.requireElement("expression", tokens); - } else if (tokens.matchToken("using")) { - var using = parser.requireElement("expression", tokens); - } - - var transition = { - to: to, - args: [targetsExpr, properties, from, to, using, over], - op: function (context, targets, properties, from, to, using, over) { - runtime.nullCheck(targets, targetsExpr); - var promises = []; - runtime.implicitLoop(targets, function (target) { - var promise = new Promise(function (resolve, reject) { - var initialTransition = target.style.transition; - if (over) { - target.style.transition = "all " + over + "ms ease-in"; - } else if (using) { - target.style.transition = using; - } else { - target.style.transition = config.defaultTransition; - } - var internalData = runtime.getInternalData(target); - var computedStyles = getComputedStyle(target); - - var initialStyles = {}; - for (var i = 0; i < computedStyles.length; i++) { - var name = computedStyles[i]; - var initialValue = computedStyles[name]; - initialStyles[name] = initialValue; - } - - // store intitial values - if (!internalData.initalStyles) { - internalData.initalStyles = initialStyles; - } - - for (var i = 0; i < properties.length; i++) { - var property = properties[i]; - var fromVal = from[i]; - if (fromVal === "computed" || fromVal == null) { - target.style[property] = initialStyles[property]; - } else { - target.style[property] = fromVal; - } - } - //console.log("transition started", transition); - - var transitionStarted = false; - var resolved = false; - - target.addEventListener( - "transitionend", - function () { - if (!resolved) { - //console.log("transition ended", transition); - target.style.transition = initialTransition; - resolved = true; - resolve(); - } - }, - { once: true } - ); - - target.addEventListener( - "transitionstart", - function () { - transitionStarted = true; - }, - { once: true } - ); - - // it no transition has started in 100ms, continue - setTimeout(function () { - if (!resolved && !transitionStarted) { - //console.log("transition ended", transition); - target.style.transition = initialTransition; - resolved = true; - resolve(); - } - }, 100); - - setTimeout(function () { - var autoProps = []; - for (var i = 0; i < properties.length; i++) { - var property = properties[i]; - var toVal = to[i]; - if (toVal === "initial") { - var propertyValue = internalData.initalStyles[property]; - target.style[property] = propertyValue; - } else { - target.style[property] = toVal; - } - //console.log("set", property, "to", target.style[property], "on", target, "value passed in : ", toVal); - } - }, 0); - }); - promises.push(promise); - }); - return Promise.all(promises).then(function () { - return runtime.findNext(transition, context); - }); - }, - }; - return transition; - } - }); - - parser.addCommand("measure", function (parser, runtime, tokens) { - if (!tokens.matchToken("measure")) return; - - var targetExpr = parsePseudopossessiveTarget(parser, runtime, tokens); - - var propsToMeasure = []; - if (!parser.commandBoundary(tokens.currentToken())) - do { - propsToMeasure.push(tokens.matchTokenType("IDENTIFIER").value); - } while (tokens.matchOpToken(",")); - - return { - properties: propsToMeasure, - args: [targetExpr], - op: function (ctx, target) { - runtime.nullCheck(target, targetExpr); - if (0 in target) target = target[0]; // not measuring multiple elts - var rect = target.getBoundingClientRect(); - var scroll = { - top: target.scrollTop, - left: target.scrollLeft, - topMax: target.scrollTopMax, - leftMax: target.scrollLeftMax, - height: target.scrollHeight, - width: target.scrollWidth, - }; - - ctx.result = { - x: rect.x, - y: rect.y, - left: rect.left, - top: rect.top, - right: rect.right, - bottom: rect.bottom, - width: rect.width, - height: rect.height, - bounds: rect, - - scrollLeft: scroll.left, - scrollTop: scroll.top, - scrollLeftMax: scroll.leftMax, - scrollTopMax: scroll.topMax, - scrollWidth: scroll.width, - scrollHeight: scroll.height, - scroll: scroll, - }; - - runtime.forEach(propsToMeasure, function (prop) { - if (prop in ctx.result) ctx.locals[prop] = ctx.result[prop]; - else throw "No such measurement as " + prop; - }); - - return runtime.findNext(this, ctx); - }, - }; - }); - - parser.addLeafExpression("closestExpr", function (parser, runtime, tokens) { - if (tokens.matchToken("closest")) { - if (tokens.matchToken("parent")) { - var parentSearch = true; - } - - var css = null; - if (tokens.currentToken().type === "ATTRIBUTE_REF") { - var attributeRef = parser.requireElement("attributeRefAccess", tokens, null); - css = "[" + attributeRef.attribute.name + "]"; - } - - if (css == null) { - var expr = parser.requireElement("expression", tokens); - if (expr.css == null) { - parser.raiseParseError(tokens, "Expected a CSS expression"); - } else { - css = expr.css; - } - } - - if (tokens.matchToken("to")) { - var to = parser.parseElement("expression", tokens); - } else { - var to = parser.parseElement("implicitMeTarget", tokens); - } - - var closestExpr = { - type: "closestExpr", - parentSearch: parentSearch, - expr: expr, - css: css, - to: to, - args: [to], - op: function (ctx, to) { - if (to == null) { - return null; - } else { - let result = []; - runtime.implicitLoop(to, function(to){ - if (parentSearch) { - result.push(to.parentElement ? to.parentElement.closest(css) : null); - } else { - result.push(to.closest(css)); - } - }) - if (runtime.shouldAutoIterate(to)) { - return result; - } else { - return result[0]; - } - } - }, - evaluate: function (context) { - return runtime.unifiedEval(this, context); - }, - }; - - if (attributeRef) { - attributeRef.root = closestExpr; - attributeRef.args = [closestExpr]; - return attributeRef; - } else { - return closestExpr; - } - } - }); - - parser.addCommand("go", function (parser, runtime, tokens) { - if (tokens.matchToken("go")) { - if (tokens.matchToken("back")) { - var back = true; - } else { - tokens.matchToken("to"); - if (tokens.matchToken("url")) { - var target = parser.requireElement("stringLike", tokens); - var url = true; - if (tokens.matchToken("in")) { - tokens.requireToken("new"); - tokens.requireToken("window"); - var newWindow = true; - } - } else { - tokens.matchToken("the"); // optional the - var verticalPosition = tokens.matchAnyToken("top", "middle", "bottom"); - var horizontalPosition = tokens.matchAnyToken("left", "center", "right"); - if (verticalPosition || horizontalPosition) { - tokens.requireToken("of"); - } - var target = parser.requireElement("unaryExpression", tokens); - - var plusOrMinus = tokens.matchAnyOpToken("+", "-"); - if (plusOrMinus) { - tokens.pushFollow("px"); - try { - var offset = parser.requireElement("expression", tokens); - } finally { - tokens.popFollow(); - } - } - tokens.matchToken("px"); // optional px - - var smoothness = tokens.matchAnyToken("smoothly", "instantly"); - - var scrollOptions = {}; - if (verticalPosition) { - if (verticalPosition.value === "top") { - scrollOptions.block = "start"; - } else if (verticalPosition.value === "bottom") { - scrollOptions.block = "end"; - } else if (verticalPosition.value === "middle") { - scrollOptions.block = "center"; - } - } - - if (horizontalPosition) { - if (horizontalPosition.value === "left") { - scrollOptions.inline = "start"; - } else if (horizontalPosition.value === "center") { - scrollOptions.inline = "center"; - } else if (horizontalPosition.value === "right") { - scrollOptions.inline = "end"; - } - } - - if (smoothness) { - if (smoothness.value === "smoothly") { - scrollOptions.behavior = "smooth"; - } else if (smoothness.value === "instantly") { - scrollOptions.behavior = "instant"; - } - } - } - } - - var goCmd = { - target: target, - args: [target, offset], - op: function (ctx, to, offset) { - if (back) { - window.history.back(); - } else if (url) { - if (to) { - if (newWindow) { - window.open(to); - } else { - window.location.href = to; - } - } - } else { - runtime.implicitLoop(to, function (target) { - - if (target === window) { - target = document.body; - } - - if(plusOrMinus) { - // a top scroll w/ an offset of some sort - var boundingRect = target.getBoundingClientRect(); - let scrollShim = document.createElement('div'); - - if (plusOrMinus.value === "-") { - var finalOffset = -offset; - } else { - var finalOffset = - -offset; - } - - scrollShim.style.position = 'absolute'; - scrollShim.style.top = (boundingRect.x + finalOffset) + "px"; - scrollShim.style.left = (boundingRect.y + finalOffset) + "px"; - scrollShim.style.height = (boundingRect.height + (2 * finalOffset)) + "px"; - scrollShim.style.width = (boundingRect.width + (2 * finalOffset)) + "px"; - scrollShim.style.zIndex = "" + Number.MIN_SAFE_INTEGER; - scrollShim.style.opacity = "0"; - - document.body.appendChild(scrollShim); - setTimeout(function () { - document.body.removeChild(scrollShim); - }, 100); - - target = scrollShim; - } - - target.scrollIntoView(scrollOptions); - }); - } - return runtime.findNext(goCmd, ctx); - }, - }; - return goCmd; - } - }); - - config.conversions.dynamicResolvers.push(function (str, node) { - if (!(str === "Values" || str.indexOf("Values:") === 0)) { - return; - } - var conversion = str.split(":")[1]; - /** @type Object */ - var result = {}; - - var implicitLoop = parser.runtime.implicitLoop.bind(parser.runtime); - - implicitLoop(node, function (/** @type HTMLInputElement */ node) { - // Try to get a value directly from this node - var input = getInputInfo(node); - - if (input !== undefined) { - result[input.name] = input.value; - return; - } - - // Otherwise, try to query all child elements of this node that *should* contain values. - if (node.querySelectorAll != undefined) { - /** @type {NodeListOf} */ - var children = node.querySelectorAll("input,select,textarea"); - children.forEach(appendValue); - } - }); - - if (conversion) { - if (conversion === "JSON") { - return JSON.stringify(result); - } else if (conversion === "Form") { - /** @ts-ignore */ - // TODO: does this work with multiple inputs of the same name? - return new URLSearchParams(result).toString(); - } else { - throw "Unknown conversion: " + conversion; - } - } else { - return result; - } - - /** - * @param {HTMLInputElement} node - */ - function appendValue(node) { - var info = getInputInfo(node); - - if (info == undefined) { - return; - } - - // If there is no value already stored in this space. - if (result[info.name] == undefined) { - result[info.name] = info.value; - return; - } - - if (Array.isArray(result[info.name]) && Array.isArray(info.value)) { - result[info.name] = [].concat(result[info.name], info.value); - return; - } - } - - /** - * @param {HTMLInputElement} node - * @returns {{name:string, value:string | string[]} | undefined} - */ - function getInputInfo(node) { - try { - /** @type {{name: string, value: string | string[]}}*/ - var result = { - name: node.name, - value: node.value, - }; - - if (result.name == undefined || result.value == undefined) { - return undefined; - } - - if (node.type == "radio" && node.checked == false) { - return undefined; - } - - if (node.type == "checkbox") { - if (node.checked == false) { - result.value = undefined; - } else if (typeof result.value === "string") { - result.value = [result.value]; - } - } - - if (node.type == "select-multiple") { - /** @type {NodeListOf} */ - var selected = node.querySelectorAll("option[selected]"); - - result.value = []; - for (var index = 0; index < selected.length; index++) { - result.value.push(selected[index].value); - } - } - return result; - } catch (e) { - return undefined; - } - } - }); - - config.conversions["HTML"] = function (value) { - var toHTML = /** @returns {string}*/ function (/** @type any*/ value) { - if (value instanceof Array) { - return value - .map(function (item) { - return toHTML(item); - }) - .join(""); - } - - if (value instanceof HTMLElement) { - return value.outerHTML; - } - - if (value instanceof NodeList) { - var result = ""; - for (var i = 0; i < value.length; i++) { - var node = value[i]; - if (node instanceof HTMLElement) { - result += node.outerHTML; - } - } - return result; - } - - if (value.toString) { - return value.toString(); - } - - return ""; - }; - - return toHTML(value); - }; - - config.conversions["Fragment"] = function (val) { - var frag = document.createDocumentFragment(); - parser.runtime.implicitLoop(val, function (val) { - if (val instanceof Node) frag.append(val); - else { - var temp = document.createElement("template"); - temp.innerHTML = val; - frag.append(temp.content); - } - }); - return frag; - }; - } - - - // Public API - - const runtime_ = new Runtime(), lexer_ = runtime_.lexer, parser_ = runtime_.parser - - /** - * - * @param {string} src - * @param {Partial} [ctx] - */ - function run(src, ctx) { - return runtime_.evaluate(src, ctx) - } - - function browserInit() { - /** @type {HTMLScriptElement[]} */ - var scripts = Array.from(globalScope.document.querySelectorAll("script[type='text/hyperscript'][src]")) - Promise.all( - scripts.map(function (script) { - return fetch(script.src) - .then(function (res) { - return res.text(); - }); - }) - ) - .then(script_values => script_values.forEach(sc => _hyperscript(sc))) - .then(() => ready(function () { - mergeMetaConfig(); - runtime_.processNode(document.documentElement); - globalScope.document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) { - runtime_.processNode(evt.detail.elt); - }); - })); - - function ready(fn) { - if (document.readyState !== "loading") { - setTimeout(fn); - } else { - document.addEventListener("DOMContentLoaded", fn); - } - } - - function getMetaConfig() { - /** @type {HTMLMetaElement} */ - var element = document.querySelector('meta[name="htmx-config"]'); - if (element) { - return parseJSON(element.content); - } else { - return null; - } - } - - function mergeMetaConfig() { - var metaConfig = getMetaConfig(); - if (metaConfig) { - Object.assign(config, metaConfig); - } - } - } - - /** - * @typedef {Object} HyperscriptAPI - * - * @property {Object} config - * @property {string} config.attributes - * @property {string} config.defaultTransition - * @property {string} config.disableSelector - * @property {typeof conversions} config.conversions - * - * @property {Object} internals - * @property {Lexer} internals.lexer - * @property {typeof Lexer} internals.Lexer - * @property {Parser} internals.parser - * @property {typeof Parser} internals.Parser - * @property {Runtime} internals.runtime - * @property {typeof Runtime} internals.Runtime - * - * @property {typeof ElementCollection} ElementCollection - * - * @property {(keyword: string, definition: ParseRule) => void} addFeature - * @property {(keyword: string, definition: ParseRule) => void} addCommand - * @property {(keyword: string, definition: ParseRule) => void} addLeafExpression - * @property {(keyword: string, definition: ParseRule) => void} addIndirectExpression - * - * @property {(src: string, ctx?: Partial) => any} evaluate - * @property {(src: string) => ASTNode} parse - * @property {(node: Element) => void} processNode - * - * @property {() => void} browserInit - * - * - * @typedef {HyperscriptAPI & ((src: string, ctx?: Partial) => any)} Hyperscript - */ - - /** - * @type {Hyperscript} - */ - const _hyperscript = Object.assign( - run, - { - config, - - use(plugin) { plugin(_hyperscript) }, - - internals: { - lexer: lexer_, parser: parser_, runtime: runtime_, - Lexer, Tokens, Parser, Runtime, - }, - ElementCollection, - - addFeature: parser_.addFeature.bind(parser_), - addCommand: parser_.addCommand.bind(parser_), - addLeafExpression: parser_.addLeafExpression.bind(parser_), - addIndirectExpression: parser_.addIndirectExpression.bind(parser_), - - evaluate: runtime_.evaluate.bind(runtime_), - parse: runtime_.parse.bind(runtime_), - processNode: runtime_.processNode.bind(runtime_), - - browserInit, - } - ) - - return _hyperscript -}) +(function(e,t){const r=t(e);if(typeof exports==="object"&&typeof exports["nodeName"]!=="string"){module.exports=r}else{e["_hyperscript"]=r;if("document"in e)e["_hyperscript"].browserInit()}})(typeof self!=="undefined"?self:this,(e=>{"use strict";const t={dynamicResolvers:[function(e,t){if(e==="Fixed"){return Number(t).toFixed()}else if(e.indexOf("Fixed:")===0){let r=e.split(":")[1];return Number(t).toFixed(parseInt(r))}}],String:function(e){if(e.toString){return e.toString()}else{return""+e}},Int:function(e){return parseInt(e)},Float:function(e){return parseFloat(e)},Number:function(e){return Number(e)},Date:function(e){return new Date(e)},Array:function(e){return Array.from(e)},JSON:function(e){return JSON.stringify(e)},Object:function(e){if(e instanceof String){e=e.toString()}if(typeof e==="string"){return JSON.parse(e)}else{return Object.assign({},e)}}};const r={attributes:"_, script, data-script",defaultTransition:"all 500ms ease-in",disableSelector:"[disable-scripting], [data-disable-scripting]",hideShowStrategies:{},conversions:t};class n{static OP_TABLE={"+":"PLUS","-":"MINUS","*":"MULTIPLY","/":"DIVIDE",".":"PERIOD","..":"ELLIPSIS","\\":"BACKSLASH",":":"COLON","%":"PERCENT","|":"PIPE","!":"EXCLAMATION","?":"QUESTION","#":"POUND","&":"AMPERSAND",$:"DOLLAR",";":"SEMI",",":"COMMA","(":"L_PAREN",")":"R_PAREN","<":"L_ANG",">":"R_ANG","<=":"LTE_ANG",">=":"GTE_ANG","==":"EQ","===":"EQQ","!=":"NEQ","!==":"NEQQ","{":"L_BRACE","}":"R_BRACE","[":"L_BRACKET","]":"R_BRACKET","=":"EQUALS"};static isValidCSSClassChar(e){return n.isAlpha(e)||n.isNumeric(e)||e==="-"||e==="_"||e===":"}static isValidCSSIDChar(e){return n.isAlpha(e)||n.isNumeric(e)||e==="-"||e==="_"||e===":"}static isWhitespace(e){return e===" "||e==="\t"||n.isNewline(e)}static positionString(e){return"[Line: "+e.line+", Column: "+e.column+"]"}static isNewline(e){return e==="\r"||e==="\n"}static isNumeric(e){return e>="0"&&e<="9"}static isAlpha(e){return e>="a"&&e<="z"||e>="A"&&e<="Z"}static isIdentifierChar(e,t){return e==="_"||e==="$"}static isReservedChar(e){return e==="`"||e==="^"}static isValidSingleQuoteStringStart(e){if(e.length>0){var t=e[e.length-1];if(t.type==="IDENTIFIER"||t.type==="CLASS_REF"||t.type==="ID_REF"){return false}if(t.op&&(t.value===">"||t.value===")")){return false}}return true}static tokenize(e,t){var r=[];var a=e;var o=0;var s=0;var u=1;var l="";var c=0;function f(){return t&&c===0}while(o=0){return this.consumeToken()}}requireToken(e,t){var r=this.matchToken(e,t);if(r){return r}else{this.raiseError(this,"Expected '"+e+"' but found '"+this.currentToken().value+"'")}}peekToken(e,t,r){t=t||0;r=r||"IDENTIFIER";return this.tokens[t]&&this.tokens[t].value===e&&this.tokens[t].type===r}matchToken(e,t){if(this.follows.indexOf(e)!==-1){return}t=t||"IDENTIFIER";if(this.currentToken()&&this.currentToken().value===e&&this.currentToken().type===t){return this.consumeToken()}}consumeToken(){var e=this.tokens.shift();this.consumed.push(e);this._lastConsumed=e;this.consumeWhitespace();return e}consumeUntil(e,t){var r=[];var n=this.token(0,true);while((t==null||n.type!==t)&&(e==null||n.value!==e)&&n.type!=="EOF"){var i=this.tokens.shift();this.consumed.push(i);r.push(n);n=this.token(0,true)}this.consumeWhitespace();return r}lastWhitespace(){if(this.consumed[this.consumed.length-1]&&this.consumed[this.consumed.length-1].type==="WHITESPACE"){return this.consumed[this.consumed.length-1].value}else{return""}}consumeUntilWhitespace(){return this.consumeUntil(null,"WHITESPACE")}hasMore(){return this.tokens.length>0}token(e,t){var r;var n=0;do{if(!t){while(this.tokens[n]&&this.tokens[n].type==="WHITESPACE"){n++}}r=this.tokens[n];e--;n++}while(e>-1);if(r){return r}else{return{type:"EOF",value:"<<>>"}}}currentToken(){return this.token(0)}lastMatch(){return this._lastConsumed}static sourceFor=function(){return this.programSource.substring(this.startToken.start,this.endToken.end)};static lineFor=function(){return this.programSource.split("\n")[this.startToken.line-1]};follows=[];pushFollow(e){this.follows.push(e)}popFollow(){this.follows.pop()}clearFollows(){var e=this.follows;this.follows=[];return e}restoreFollows(e){this.follows=e}}class a{constructor(e){this.runtime=e;this.possessivesDisabled=false;this.addGrammarElement("feature",(function(e,t,r){if(r.matchOpToken("(")){var n=e.requireElement("feature",r);r.requireOpToken(")");return n}var i=e.FEATURES[r.currentToken().value||""];if(i){return i(e,t,r)}}));this.addGrammarElement("command",(function(e,t,r){if(r.matchOpToken("(")){const t=e.requireElement("command",r);r.requireOpToken(")");return t}var n=e.COMMANDS[r.currentToken().value||""];let i;if(n){i=n(e,t,r)}else if(r.currentToken().type==="IDENTIFIER"){i=e.parseElement("pseudoCommand",r)}if(i){return e.parseElement("indirectStatement",r,i)}return i}));this.addGrammarElement("commandList",(function(e,t,r){if(r.hasMore()){var n=e.parseElement("command",r);if(n){r.matchToken("then");const t=e.parseElement("commandList",r);if(t)n.next=t;return n}}return{type:"emptyCommandListCommand",op:function(e){return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}));this.addGrammarElement("leaf",(function(e,t,r){var n=e.parseAnyOf(e.LEAF_EXPRESSIONS,r);if(n==null){return e.parseElement("symbol",r)}return n}));this.addGrammarElement("indirectExpression",(function(e,t,r,n){for(var i=0;i{this.unifiedExec(e,t)})).catch((e=>{this.unifiedExec({op:function(){throw e}},t)}));return}else if(r===o.HALT){if(t.meta.finallyHandler&&!t.meta.handlingFinally){t.meta.handlingFinally=true;e=t.meta.finallyHandler}else{if(t.meta.onHalt){t.meta.onHalt()}if(t.meta.currentException){if(t.meta.reject){t.meta.reject(t.meta.currentException);return}else{throw t.meta.currentException}}else{return}}}else{e=r}}}unifiedEval(e,t){var r=[t];var n=false;var i=false;if(e.args){for(var a=0;a{r=this.wrapArrays(r);Promise.all(r).then((function(r){if(i){this.unwrapAsyncs(r)}try{var a=e.op.apply(e,r);t(a)}catch(e){n(e)}})).catch((function(e){n(e)}))}))}else{if(i){this.unwrapAsyncs(r)}return e.op.apply(e,r)}}_scriptAttrs=null;getScriptAttributes(){if(this._scriptAttrs==null){this._scriptAttrs=r.attributes.replace(/ /g,"").split(",")}return this._scriptAttrs}getScript(e){for(var t=0;t{this.initElement(e,e instanceof HTMLScriptElement&&e.type==="text/hyperscript"?document.body:e)}))}}initElement(e,t){if(e.closest&&e.closest(r.disableSelector)){return}var n=this.getInternalData(e);if(!n.initialized){var i=this.getScript(e);if(i){try{n.initialized=true;n.script=i;const r=this.lexer,s=this.parser;var a=r.tokenize(i);var o=s.parseHyperScript(a);if(!o)return;o.apply(t||e,e);setTimeout((()=>{this.triggerEvent(t||e,"load",{hyperscript:true})}),1)}catch(t){this.triggerEvent(e,"exception",{error:t});console.error("hyperscript errors were found on the following element:",e,"\n\n",t.message,t.stack)}}}}internalDataMap=new WeakMap;getInternalData(e){var t=this.internalDataMap.get(e);if(typeof t==="undefined"){this.internalDataMap.set(e,t={})}return t}typeCheck(e,t,r){if(e==null&&r){return true}var n=Object.prototype.toString.call(e).slice(8,-1);return n===t}getElementScope(e){var t=e.meta&&e.meta.owner;if(t){var r=this.getInternalData(t);var n="elementScope";if(e.meta.feature&&e.meta.feature.behavior){n=e.meta.feature.behavior+"Scope"}var i=h(r,n);return i}else{return{}}}isReservedWord(e){return["meta","it","result","locals","event","target","detail","sender","body"].includes(e)}isHyperscriptContext(e){return e instanceof f}resolveSymbol(t,r,n){if(t==="me"||t==="my"||t==="I"){return r.me}if(t==="it"||t==="its"||t==="result"){return r.result}if(t==="you"||t==="your"||t==="yourself"){return r.you}else{if(n==="global"){return e[t]}else if(n==="element"){var i=this.getElementScope(r);return i[t]}else if(n==="local"){return r.locals[t]}else{if(r.meta&&r.meta.context){var a=r.meta.context[t];if(typeof a!=="undefined"){return a}if(r.meta.context.detail){a=r.meta.context.detail[t];if(typeof a!=="undefined"){return a}}}if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)){var o=r.locals[t]}else{var o=r[t]}if(typeof o!=="undefined"){return o}else{var i=this.getElementScope(r);o=i[t];if(typeof o!=="undefined"){return o}else{return e[t]}}}}}setSymbol(t,r,n,i){if(n==="global"){e[t]=i}else if(n==="element"){var a=this.getElementScope(r);a[t]=i}else if(n==="local"){r.locals[t]=i}else{if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)&&typeof r.locals[t]!=="undefined"){r.locals[t]=i}else{var a=this.getElementScope(r);var o=a[t];if(typeof o!=="undefined"){a[t]=i}else{if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)){r.locals[t]=i}else{r[t]=i}}}}}findNext(e,t){if(e){if(e.resolveNext){return e.resolveNext(t)}else if(e.next){return e.next}else{return this.findNext(e.parent,t)}}}flatGet(e,t,r){if(e!=null){var n=r(e,t);if(typeof n!=="undefined"){return n}if(this.shouldAutoIterate(e)){var i=[];for(var a of e){var o=r(a,t);i.push(o)}return i}}}resolveProperty(e,t){return this.flatGet(e,t,((e,t)=>e[t]))}resolveAttribute(e,t){return this.flatGet(e,t,((e,t)=>e.getAttribute&&e.getAttribute(t)))}resolveStyle(e,t){return this.flatGet(e,t,((e,t)=>e.style&&e.style[t]))}resolveComputedStyle(e,t){return this.flatGet(e,t,((e,t)=>getComputedStyle(e).getPropertyValue(t)))}assignToNamespace(t,r,n,i){let a;if(typeof document!=="undefined"&&t===document.body){a=e}else{a=this.getHyperscriptFeatures(t)}var o;while((o=r.shift())!==undefined){var s=a[o];if(s==null){s={};a[o]=s}a=s}a[n]=i}getHyperTrace(e,t){var r=[];var n=e;while(n.meta.caller){n=n.meta.caller}if(n.meta.traceMap){return n.meta.traceMap.get(t,r)}}registerHyperTrace(e,t){var r=[];var n=null;while(e!=null){r.push(e);n=e;e=e.meta.caller}if(n.meta.traceMap==null){n.meta.traceMap=new Map}if(!n.meta.traceMap.get(t)){var i={trace:r,print:function(e){e=e||console.error;e("hypertrace /// ");var t=0;for(var n=0;n",i.meta.feature.displayName.padEnd(t+2),"-",i.meta.owner)}}};n.meta.traceMap.set(t,i)}}escapeSelector(e){return e.replace(/:/g,(function(e){return"\\"+e}))}nullCheck(e,t){if(e==null){throw new Error("'"+t.sourceFor()+"' is null")}}isEmpty(e){return e==undefined||e.length===0}doesExist(e){if(e==null){return false}if(this.shouldAutoIterate(e)){for(const t of e){return true}return false}return true}getRootNode(e){if(e&&e instanceof Node){var t=e.getRootNode();if(t instanceof Document||t instanceof ShadowRoot)return t}return document}getEventQueueFor(e,t){let r=this.getInternalData(e);var n=r.eventQueues;if(n==null){n=new Map;r.eventQueues=n}var i=n.get(t);if(i==null){i={queue:[],executing:false};n.set(t,i)}return i}beepValueToConsole(e,t,r){if(this.triggerEvent(e,"hyperscript:beep",{element:e,expression:t,value:r})){var n;if(r){if(r instanceof m){n="ElementCollection"}else if(r.constructor){n=r.constructor.name}else{n="unknown"}}else{n="object (null)"}var a=r;if(n==="String"){a='"'+a+'"'}else if(r instanceof m){a=Array.from(r)}console.log("///_ BEEP! The expression ("+i.sourceFor.call(t).replace("beep! ","")+") evaluates to:",a,"of type "+n)}}hyperscriptUrl="document"in e&&document.currentScript?document.currentScript.src:null}function s(){let e=document.cookie.split("; ").map((e=>{let t=e.split("=");return{name:t[0],value:decodeURIComponent(t[1])}}));return e}function u(e){document.cookie=e+"=;expires=Thu, 01 Jan 1970 00:00:00 GMT"}function l(){for(const e of s()){u(e.name)}}const c=new Proxy({},{get(e,t){if(t==="then"||t==="asyncWrapper"){return null}else if(t==="length"){return s().length}else if(t==="clear"){return u}else if(t==="clearAll"){return l}else if(typeof t==="string"){if(!isNaN(t)){return s()[parseInt(t)]}else{let e=document.cookie.split("; ").find((e=>e.startsWith(t+"=")))?.split("=")[1];if(e){return decodeURIComponent(e)}}}else if(t===Symbol.iterator){return s()[t]}},set(e,t,r){var n=null;if("string"===typeof r){n=encodeURIComponent(r);n+=";samesite=lax"}else{n=encodeURIComponent(r.value);if(r.expires){n+=";expires="+r.maxAge}if(r.maxAge){n+=";max-age="+r.maxAge}if(r.partitioned){n+=";partitioned="+r.partitioned}if(r.path){n+=";path="+r.path}if(r.samesite){n+=";samesite="+r.path}if(r.secure){n+=";secure="+r.path}}document.cookie=t+"="+r;return true}});class f{constructor(t,r,n,i,a){this.meta={parser:a.parser,lexer:a.lexer,runtime:a,owner:t,feature:r,iterators:{},ctx:this};this.locals={cookies:c};this.me=n,this.you=undefined;this.result=undefined;this.event=i;this.target=i?i.target:null;this.detail=i?i.detail:null;this.sender=i?i.detail?i.detail.sender:null:null;this.body="document"in e?document.body:null;a.addFeatures(t,this)}}class m{constructor(e,t,r){this._css=e;this.relativeToElement=t;this.escape=r;this[p]=true}get css(){if(this.escape){return o.prototype.escapeSelector(this._css)}else{return this._css}}get className(){return this._css.substr(1)}get id(){return this.className()}contains(e){for(let t of this){if(t.contains(e)){return true}}return false}get length(){return this.selectMatches().length}[Symbol.iterator](){let e=this.selectMatches();return e[Symbol.iterator]()}selectMatches(){let e=o.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css);return e}}const p=Symbol();function h(e,t){var r=e[t];if(r){return r}else{var n={};e[t]=n;return n}}function v(e){try{return JSON.parse(e)}catch(e){d(e);return null}}function d(e){if(console.error){console.error(e)}else if(console.log){console.log("ERROR: ",e)}}function E(e,t){return new(e.bind.apply(e,[e].concat(t)))}function T(t){t.addLeafExpression("parenthesized",(function(e,t,r){if(r.matchOpToken("(")){var n=r.clearFollows();try{var i=e.requireElement("expression",r)}finally{r.restoreFollows(n)}r.requireOpToken(")");return i}}));t.addLeafExpression("string",(function(e,t,r){var i=r.matchTokenType("STRING");if(!i)return;var a=i.value;var o;if(i.template){var s=n.tokenize(a,true);o=e.parseStringTemplate(s)}else{o=[]}return{type:"string",token:i,args:o,op:function(e){var t="";for(var r=1;re instanceof Element))}get css(){let e="",t=0;for(const r of this.templateParts){if(r instanceof Element){e+="[data-hs-query-id='"+t+++"']"}else e+=r}return e}[Symbol.iterator](){this.elements.forEach(((e,t)=>e.dataset.hsQueryId=t));const e=super[Symbol.iterator]();this.elements.forEach((e=>e.removeAttribute("data-hs-query-id")));return e}}t.addLeafExpression("queryRef",(function(e,t,i){var a=i.matchOpToken("<");if(!a)return;var o=i.consumeUntil("/");i.requireOpToken("/");i.requireOpToken(">");var s=o.map((function(e){if(e.type==="STRING"){return'"'+e.value+'"'}else{return e.value}})).join("");var u,l,c;if(s.indexOf("$")>=0){u=true;l=n.tokenize(s,true);c=e.parseStringTemplate(l)}return{type:"queryRef",css:s,args:c,op:function(e,...t){if(u){return new r(s,e.me,t)}else{return new m(s,e.me)}},evaluate:function(e){return t.unifiedEval(this,e)}}}));t.addLeafExpression("attributeRef",(function(e,t,r){var n=r.matchTokenType("ATTRIBUTE_REF");if(!n)return;if(!n.value)return;var i=n.value;if(i.indexOf("[")===0){var a=i.substring(2,i.length-1)}else{var a=i.substring(1)}var o="["+a+"]";var s=a.split("=");var u=s[0];var l=s[1];if(l){if(l.indexOf('"')===0){l=l.substring(1,l.length-1)}}return{type:"attributeRef",name:u,css:o,value:l,op:function(e){var t=e.you||e.me;if(t){return t.getAttribute(u)}},evaluate:function(e){return t.unifiedEval(this,e)}}}));t.addLeafExpression("styleRef",(function(e,t,r){var n=r.matchTokenType("STYLE_REF");if(!n)return;if(!n.value)return;var i=n.value.substr(1);if(i.startsWith("computed-")){i=i.substr("computed-".length);return{type:"computedStyleRef",name:i,op:function(e){var r=e.you||e.me;if(r){return t.resolveComputedStyle(r,i)}},evaluate:function(e){return t.unifiedEval(this,e)}}}else{return{type:"styleRef",name:i,op:function(e){var r=e.you||e.me;if(r){return t.resolveStyle(r,i)}},evaluate:function(e){return t.unifiedEval(this,e)}}}}));t.addGrammarElement("objectKey",(function(e,t,r){var n;if(n=r.matchTokenType("STRING")){return{type:"objectKey",key:n.value,evaluate:function(){return n.value}}}else if(r.matchOpToken("[")){var i=e.parseElement("expression",r);r.requireOpToken("]");return{type:"objectKey",expr:i,args:[i],op:function(e,t){return t},evaluate:function(e){return t.unifiedEval(this,e)}}}else{var a="";do{n=r.matchTokenType("IDENTIFIER")||r.matchOpToken("-");if(n)a+=n.value}while(n);return{type:"objectKey",key:a,evaluate:function(){return a}}}}));t.addLeafExpression("objectLiteral",(function(e,t,r){if(!r.matchOpToken("{"))return;var n=[];var i=[];if(!r.matchOpToken("}")){do{var a=e.requireElement("objectKey",r);r.requireOpToken(":");var o=e.requireElement("expression",r);i.push(o);n.push(a)}while(r.matchOpToken(","));r.requireOpToken("}")}return{type:"objectLiteral",args:[n,i],op:function(e,t,r){var n={};for(var i=0;i");var a=e.requireElement("expression",r);return{type:"blockLiteral",args:n,expr:a,evaluate:function(e){var t=function(){for(var t=0;t=0;a--){var o=i[a];if(o.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return o}}if(n){return i[i.length-1]}};var l=function(e,t,r,n){var i=[];o.prototype.forEach(t,(function(t){if(t.matches(r)||t===e){i.push(t)}}));for(var a=0;a","<=",">=","==","===","!=","!==");var a=i?i.value:null;var o=true;var s=false;if(a==null){if(r.matchToken("is")||r.matchToken("am")){if(r.matchToken("not")){if(r.matchToken("in")){a="not in"}else if(r.matchToken("a")){a="not a";s=true}else if(r.matchToken("empty")){a="not empty";o=false}else{if(r.matchToken("really")){a="!=="}else{a="!="}if(r.matchToken("equal")){r.matchToken("to")}}}else if(r.matchToken("in")){a="in"}else if(r.matchToken("a")){a="a";s=true}else if(r.matchToken("empty")){a="empty";o=false}else if(r.matchToken("less")){r.requireToken("than");if(r.matchToken("or")){r.requireToken("equal");r.requireToken("to");a="<="}else{a="<"}}else if(r.matchToken("greater")){r.requireToken("than");if(r.matchToken("or")){r.requireToken("equal");r.requireToken("to");a=">="}else{a=">"}}else{if(r.matchToken("really")){a="==="}else{a="=="}if(r.matchToken("equal")){r.matchToken("to")}}}else if(r.matchToken("equals")){a="=="}else if(r.matchToken("really")){r.requireToken("equals");a="==="}else if(r.matchToken("exist")||r.matchToken("exists")){a="exist";o=false}else if(r.matchToken("matches")||r.matchToken("match")){a="match"}else if(r.matchToken("contains")||r.matchToken("contain")){a="contain"}else if(r.matchToken("includes")||r.matchToken("include")){a="include"}else if(r.matchToken("do")||r.matchToken("does")){r.requireToken("not");if(r.matchToken("matches")||r.matchToken("match")){a="not match"}else if(r.matchToken("contains")||r.matchToken("contain")){a="not contain"}else if(r.matchToken("exist")||r.matchToken("exist")){a="not exist";o=false}else if(r.matchToken("include")){a="not include"}else{e.raiseParseError(r,"Expected matches or contains")}}}if(a){var u,l,c;if(s){u=r.requireTokenType("IDENTIFIER");l=!r.matchOpToken("!")}else if(o){c=e.requireElement("mathExpression",r);if(a==="match"||a==="not match"){c=c.css?c.css:c}}var m=n;n={type:"comparisonOperator",operator:a,typeName:u,nullOk:l,lhs:n,rhs:c,args:[n,c],op:function(e,r,n){if(a==="=="){return r==n}else if(a==="!="){return r!=n}if(a==="==="){return r===n}else if(a==="!=="){return r!==n}if(a==="match"){return r!=null&&p(m,r,n)}if(a==="not match"){return r==null||!p(m,r,n)}if(a==="in"){return n!=null&&f(c,n,r)}if(a==="not in"){return n==null||!f(c,n,r)}if(a==="contain"){return r!=null&&f(m,r,n)}if(a==="not contain"){return r==null||!f(m,r,n)}if(a==="include"){return r!=null&&f(m,r,n)}if(a==="not include"){return r==null||!f(m,r,n)}if(a==="==="){return r===n}else if(a==="!=="){return r!==n}else if(a==="<"){return r"){return r>n}else if(a==="<="){return r<=n}else if(a===">="){return r>=n}else if(a==="empty"){return t.isEmpty(r)}else if(a==="not empty"){return!t.isEmpty(r)}else if(a==="exist"){return t.doesExist(r)}else if(a==="not exist"){return!t.doesExist(r)}else if(a==="a"){return t.typeCheck(r,u.value,l)}else if(a==="not a"){return!t.typeCheck(r,u.value,l)}else{throw"Unknown comparison : "+a}},evaluate:function(e){return t.unifiedEval(this,e)}}}return n}));t.addGrammarElement("comparisonExpression",(function(e,t,r){return e.parseAnyOf(["comparisonOperator","mathExpression"],r)}));t.addGrammarElement("logicalOperator",(function(e,t,r){var n=e.parseElement("comparisonExpression",r);var i,a=null;i=r.matchToken("and")||r.matchToken("or");while(i){a=a||i;if(a.value!==i.value){e.raiseParseError(r,"You must parenthesize logical operations with different operators")}var o=e.requireElement("comparisonExpression",r);const s=i.value;n={type:"logicalOperator",operator:s,lhs:n,rhs:o,args:[n,o],op:function(e,t,r){if(s==="and"){return t&&r}else{return t||r}},evaluate:function(e){return t.unifiedEval(this,e)}};i=r.matchToken("and")||r.matchToken("or")}return n}));t.addGrammarElement("logicalExpression",(function(e,t,r){return e.parseAnyOf(["logicalOperator","mathExpression"],r)}));t.addGrammarElement("asyncExpression",(function(e,t,r){if(r.matchToken("async")){var n=e.requireElement("logicalExpression",r);var i={type:"asyncExpression",value:n,evaluate:function(e){return{asyncWrapper:true,value:this.value.evaluate(e)}}};return i}else{return e.parseElement("logicalExpression",r)}}));t.addGrammarElement("expression",(function(e,t,r){r.matchToken("the");return e.parseElement("asyncExpression",r)}));t.addGrammarElement("assignableExpression",(function(e,t,r){r.matchToken("the");var n=e.parseElement("primaryExpression",r);if(n&&(n.type==="symbol"||n.type==="ofExpression"||n.type==="propertyAccess"||n.type==="attributeRefAccess"||n.type==="attributeRef"||n.type==="styleRef"||n.type==="arrayIndex"||n.type==="possessive")){return n}else{e.raiseParseError(r,"A target expression must be writable. The expression type '"+(n&&n.type)+"' is not.")}return n}));t.addGrammarElement("hyperscript",(function(e,t,r){var n=[];if(r.hasMore()){while(e.featureStart(r.currentToken())||r.currentToken().value==="("){var i=e.requireElement("feature",r);n.push(i);r.matchToken("end")}}return{type:"hyperscript",features:n,apply:function(e,t,r){for(const i of n){i.install(e,t,r)}}}}));var v=function(e){var t=[];if(e.token(0).value==="("&&(e.token(1).value===")"||e.token(2).value===","||e.token(2).value===")")){e.matchOpToken("(");do{t.push(e.requireTokenType("IDENTIFIER"))}while(e.matchOpToken(","));e.requireOpToken(")")}return t};t.addFeature("on",(function(e,t,r){if(!r.matchToken("on"))return;var n=false;if(r.matchToken("every")){n=true}var i=[];var a=null;do{var o=e.requireElement("eventName",r,"Expected event name");var s=o.evaluate();if(a){a=a+" or "+s}else{a="on "+s}var u=v(r);var l=null;if(r.matchOpToken("[")){l=e.requireElement("expression",r);r.requireOpToken("]")}var c,f,m;if(r.currentToken().type==="NUMBER"){var p=r.consumeToken();if(!p.value)return;c=parseInt(p.value);if(r.matchToken("to")){var h=r.consumeToken();if(!h.value)return;f=parseInt(h.value)}else if(r.matchToken("and")){m=true;r.requireToken("on")}}var d,E;if(s==="intersection"){d={};if(r.matchToken("with")){d["with"]=e.requireElement("expression",r).evaluate()}if(r.matchToken("having")){do{if(r.matchToken("margin")){d["rootMargin"]=e.requireElement("stringLike",r).evaluate()}else if(r.matchToken("threshold")){d["threshold"]=e.requireElement("expression",r).evaluate()}else{e.raiseParseError(r,"Unknown intersection config specification")}}while(r.matchToken("and"))}}else if(s==="mutation"){E={};if(r.matchToken("of")){do{if(r.matchToken("anything")){E["attributes"]=true;E["subtree"]=true;E["characterData"]=true;E["childList"]=true}else if(r.matchToken("childList")){E["childList"]=true}else if(r.matchToken("attributes")){E["attributes"]=true;E["attributeOldValue"]=true}else if(r.matchToken("subtree")){E["subtree"]=true}else if(r.matchToken("characterData")){E["characterData"]=true;E["characterDataOldValue"]=true}else if(r.currentToken().type==="ATTRIBUTE_REF"){var T=r.consumeToken();if(E["attributeFilter"]==null){E["attributeFilter"]=[]}if(T.value.indexOf("@")==0){E["attributeFilter"].push(T.value.substring(1))}else{e.raiseParseError(r,"Only shorthand attribute references are allowed here")}}else{e.raiseParseError(r,"Unknown mutation config specification")}}while(r.matchToken("or"))}else{E["attributes"]=true;E["characterData"]=true;E["childList"]=true}}var y=null;var k=false;if(r.matchToken("from")){if(r.matchToken("elsewhere")){k=true}else{r.pushFollow("or");try{y=e.requireElement("expression",r)}finally{r.popFollow()}if(!y){e.raiseParseError(r,'Expected either target value or "elsewhere".')}}}if(y===null&&k===false&&r.matchToken("elsewhere")){k=true}if(r.matchToken("in")){var x=e.parseElement("unaryExpression",r)}if(r.matchToken("debounced")){r.requireToken("at");var g=e.requireElement("unaryExpression",r);var b=g.evaluate({})}else if(r.matchToken("throttled")){r.requireToken("at");var g=e.requireElement("unaryExpression",r);var w=g.evaluate({})}i.push({execCount:0,every:n,on:s,args:u,filter:l,from:y,inExpr:x,elsewhere:k,startCount:c,endCount:f,unbounded:m,debounceTime:b,throttleTime:w,mutationSpec:E,intersectionSpec:d,debounced:undefined,lastExec:undefined})}while(r.matchToken("or"));var S=true;if(!n){if(r.matchToken("queue")){if(r.matchToken("all")){var q=true;var S=false}else if(r.matchToken("first")){var N=true}else if(r.matchToken("none")){var C=true}else{r.requireToken("last")}}}var I=e.requireElement("commandList",r);e.ensureTerminated(I);var R,A;if(r.matchToken("catch")){R=r.requireTokenType("IDENTIFIER").value;A=e.requireElement("commandList",r);e.ensureTerminated(A)}if(r.matchToken("finally")){var O=e.requireElement("commandList",r);e.ensureTerminated(O)}var L={displayName:a,events:i,start:I,every:n,execCount:0,errorHandler:A,errorSymbol:R,execute:function(e){let r=t.getEventQueueFor(e.me,L);if(r.executing&&n===false){if(C||N&&r.queue.length>0){return}if(S){r.queue.length=0}r.queue.push(e);return}L.execCount++;r.executing=true;e.meta.onHalt=function(){r.executing=false;var e=r.queue.shift();if(e){setTimeout((function(){L.execute(e)}),1)}};e.meta.reject=function(r){console.error(r.message?r.message:r);var n=t.getHyperTrace(e,r);if(n){n.print()}t.triggerEvent(e.me,"exception",{error:r})};I.execute(e)},install:function(e,r){for(const r of L.events){var n;if(r.elsewhere){n=[document]}else if(r.from){n=r.from.evaluate(t.makeContext(e,L,e,null))}else{n=[e]}t.implicitLoop(n,(function(n){var i=r.on;if(n==null){console.warn("'%s' feature ignored because target does not exists:",a,e);return}if(r.mutationSpec){i="hyperscript:mutation";const e=new MutationObserver((function(e,r){if(!L.executing){t.triggerEvent(n,i,{mutationList:e,observer:r})}}));e.observe(n,r.mutationSpec)}if(r.intersectionSpec){i="hyperscript:intersection";const e=new IntersectionObserver((function(r){for(const o of r){var a={observer:e};a=Object.assign(a,o);a["intersecting"]=o.isIntersecting;t.triggerEvent(n,i,a)}}),r.intersectionSpec);e.observe(n)}var o=n.addEventListener||n.on;o.call(n,i,(function a(o){if(typeof Node!=="undefined"&&e instanceof Node&&n!==e&&!e.isConnected){n.removeEventListener(i,a);return}var s=t.makeContext(e,L,e,o);if(r.elsewhere&&e.contains(o.target)){return}if(r.from){s.result=n}for(const e of r.args){let t=s.event[e.value];if(t!==undefined){s.locals[e.value]=t}else if("detail"in s.event){s.locals[e.value]=s.event["detail"][e.value]}}s.meta.errorHandler=A;s.meta.errorSymbol=R;s.meta.finallyHandler=O;if(r.filter){var u=s.meta.context;s.meta.context=s.event;try{var l=r.filter.evaluate(s);if(l){}else{return}}finally{s.meta.context=u}}if(r.inExpr){var c=o.target;while(true){if(c.matches&&c.matches(r.inExpr.css)){s.result=c;break}else{c=c.parentElement;if(c==null){return}}}}r.execCount++;if(r.startCount){if(r.endCount){if(r.execCountr.endCount){return}}else if(r.unbounded){if(r.execCount{var a=false;for(const s of i){var o=n=>{e.result=n;if(s.args){for(const t of s.args){e.locals[t.value]=n[t.value]||(n.detail?n.detail[t.value]:null)}}if(!a){a=true;r(t.findNext(this,e))}};if(s.name){n.addEventListener(s.name,o,{once:true})}else if(s.time!=null){setTimeout(o,s.time,s.time)}}}))}};return n}else{var s;if(r.matchToken("a")){r.requireToken("tick");s=0}else{s=e.requireElement("expression",r)}n={type:"waitCmd",time:s,args:[s],op:function(e,r){return new Promise((n=>{setTimeout((()=>{n(t.findNext(this,e))}),r)}))},execute:function(e){return t.unifiedExec(this,e)}};return n}}));t.addGrammarElement("dotOrColonPath",(function(e,t,r){var n=r.matchTokenType("IDENTIFIER");if(n){var i=[n.value];var a=r.matchOpToken(".")||r.matchOpToken(":");if(a){do{i.push(r.requireTokenType("IDENTIFIER","NUMBER").value)}while(r.matchOpToken(a.value))}return{type:"dotOrColonPath",path:i,evaluate:function(){return i.join(a?a.value:"")}}}}));t.addGrammarElement("eventName",(function(e,t,r){var n;if(n=r.matchTokenType("STRING")){return{evaluate:function(){return n.value}}}return e.parseElement("dotOrColonPath",r)}));function d(e,t,r,n){var i=t.requireElement("eventName",n);var a=t.parseElement("namedArgumentList",n);if(e==="send"&&n.matchToken("to")||e==="trigger"&&n.matchToken("on")){var o=t.requireElement("expression",n)}else{var o=t.requireElement("implicitMeTarget",n)}var s={eventName:i,details:a,to:o,args:[o,i,a],op:function(e,t,n,i){r.nullCheck(t,o);r.implicitLoop(t,(function(t){r.triggerEvent(t,n,i,e.me)}));return r.findNext(s,e)}};return s}t.addCommand("trigger",(function(e,t,r){if(r.matchToken("trigger")){return d("trigger",e,t,r)}}));t.addCommand("send",(function(e,t,r){if(r.matchToken("send")){return d("send",e,t,r)}}));var T=function(e,t,r,n){if(n){if(e.commandBoundary(r.currentToken())){e.raiseParseError(r,"'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.")}else{var i=e.requireElement("expression",r)}}var a={value:i,args:[i],op:function(e,r){var n=e.meta.resolve;e.meta.returned=true;e.meta.returnValue=r;if(n){if(r){n(r)}else{n()}}return t.HALT}};return a};t.addCommand("return",(function(e,t,r){if(r.matchToken("return")){return T(e,t,r,true)}}));t.addCommand("exit",(function(e,t,r){if(r.matchToken("exit")){return T(e,t,r,false)}}));t.addCommand("halt",(function(e,t,r){if(r.matchToken("halt")){if(r.matchToken("the")){r.requireToken("event");if(r.matchOpToken("'")){r.requireToken("s")}var n=true}if(r.matchToken("bubbling")){var i=true}else if(r.matchToken("default")){var a=true}var o=T(e,t,r,false);var s={keepExecuting:true,bubbling:i,haltDefault:a,exit:o,op:function(e){if(e.event){if(i){e.event.stopPropagation()}else if(a){e.event.preventDefault()}else{e.event.stopPropagation();e.event.preventDefault()}if(n){return t.findNext(this,e)}else{return o}}}};return s}}));t.addCommand("log",(function(e,t,r){if(!r.matchToken("log"))return;var n=[e.parseElement("expression",r)];while(r.matchOpToken(",")){n.push(e.requireElement("expression",r))}if(r.matchToken("with")){var i=e.requireElement("expression",r)}var a={exprs:n,withExpr:i,args:[i,n],op:function(e,r,n){if(r){r.apply(null,n)}else{console.log.apply(null,n)}return t.findNext(this,e)}};return a}));t.addCommand("beep!",(function(e,t,r){if(!r.matchToken("beep!"))return;var n=[e.parseElement("expression",r)];while(r.matchOpToken(",")){n.push(e.requireElement("expression",r))}var i={exprs:n,args:[n],op:function(e,r){for(let i=0;i{if(!r.matchToken("pick"))return;r.matchToken("the");if(r.matchToken("item")||r.matchToken("items")||r.matchToken("character")||r.matchToken("characters")){const n=g(e,t,r);r.requireToken("from");const i=e.requireElement("expression",r);return{args:[i,n.from,n.to],op(e,r,i,a){if(n.toEnd)a=r.length;if(!n.includeStart)i++;if(n.includeEnd)a++;if(a==null||a==undefined)a=i+1;e.result=r.slice(i,a);return t.findNext(this,e)}}}if(r.matchToken("match")){r.matchToken("of");const n=e.parseElement("expression",r);let i="";if(r.matchOpToken("|")){i=r.requireToken("identifier").value}r.requireToken("from");const a=e.parseElement("expression",r);return{args:[a,n],op(e,r,n){e.result=new RegExp(n,i).exec(r);return t.findNext(this,e)}}}if(r.matchToken("matches")){r.matchToken("of");const n=e.parseElement("expression",r);let i="gu";if(r.matchOpToken("|")){i="g"+r.requireToken("identifier").value.replace("g","")}console.log("flags",i);r.requireToken("from");const a=e.parseElement("expression",r);return{args:[a,n],op(e,r,n){e.result=new w(n,i,r);return t.findNext(this,e)}}}}));t.addCommand("increment",(function(e,t,r){if(!r.matchToken("increment"))return;var n;var i=e.parseElement("assignableExpression",r);if(r.matchToken("by")){n=e.requireElement("expression",r)}var a={type:"implicitIncrementOp",target:i,args:[i,n],op:function(e,t,r){t=t?parseFloat(t):0;r=n?parseFloat(r):1;var i=t+r;e.result=i;return i},evaluate:function(e){return t.unifiedEval(this,e)}};return k(e,t,r,i,a)}));t.addCommand("decrement",(function(e,t,r){if(!r.matchToken("decrement"))return;var n;var i=e.parseElement("assignableExpression",r);if(r.matchToken("by")){n=e.requireElement("expression",r)}var a={type:"implicitDecrementOp",target:i,args:[i,n],op:function(e,t,r){t=t?parseFloat(t):0;r=n?parseFloat(r):1;var i=t-r;e.result=i;return i},evaluate:function(e){return t.unifiedEval(this,e)}};return k(e,t,r,i,a)}));function S(e,t){var r="text";var n;e.matchToken("a")||e.matchToken("an");if(e.matchToken("json")||e.matchToken("Object")){r="json"}else if(e.matchToken("response")){r="response"}else if(e.matchToken("html")){r="html"}else if(e.matchToken("text")){}else{n=t.requireElement("dotOrColonPath",e).evaluate()}return{type:r,conversion:n}}t.addCommand("fetch",(function(e,t,r){if(!r.matchToken("fetch"))return;var n=e.requireElement("stringLike",r);if(r.matchToken("as")){var i=S(r,e)}if(r.matchToken("with")&&r.currentToken().value!=="{"){var a=e.parseElement("nakedNamedArgumentList",r)}else{var a=e.parseElement("objectLiteral",r)}if(i==null&&r.matchToken("as")){i=S(r,e)}var o=i?i.type:"text";var s=i?i.conversion:null;var u={url:n,argExpressions:a,args:[n,a],op:function(e,r,n){var i=n||{};i["sender"]=e.me;i["headers"]=i["headers"]||{};var a=new AbortController;let l=e.me.addEventListener("fetch:abort",(function(){a.abort()}),{once:true});i["signal"]=a.signal;t.triggerEvent(e.me,"hyperscript:beforeFetch",i);t.triggerEvent(e.me,"fetch:beforeRequest",i);n=i;var c=false;if(n.timeout){setTimeout((function(){if(!c){a.abort()}}),n.timeout)}return fetch(r,n).then((function(r){let n={response:r};t.triggerEvent(e.me,"fetch:afterResponse",n);r=n.response;if(o==="response"){e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}if(o==="json"){return r.json().then((function(r){e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}))}return r.text().then((function(r){if(s)r=t.convertValue(r,s);if(o==="html")r=t.convertValue(r,"Fragment");e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}))})).catch((function(r){t.triggerEvent(e.me,"fetch:error",{reason:r});throw r})).finally((function(){e.me.removeEventListener("fetch:abort",l)}))}};return u}))}function y(e){e.addCommand("settle",(function(e,t,r){if(r.matchToken("settle")){if(!e.commandBoundary(r.currentToken())){var n=e.requireElement("expression",r)}else{var n=e.requireElement("implicitMeTarget",r)}var i={type:"settleCmd",args:[n],op:function(e,r){t.nullCheck(r,n);var a=null;var o=false;var s=false;var u=new Promise((function(e){a=e}));r.addEventListener("transitionstart",(function(){s=true}),{once:true});setTimeout((function(){if(!s&&!o){a(t.findNext(i,e))}}),500);r.addEventListener("transitionend",(function(){if(!o){a(t.findNext(i,e))}}),{once:true});return u},execute:function(e){return t.unifiedExec(this,e)}};return i}}));e.addCommand("add",(function(e,t,r){if(r.matchToken("add")){var n=e.parseElement("classRef",r);var i=null;var a=null;if(n==null){i=e.parseElement("attributeRef",r);if(i==null){a=e.parseElement("styleLiteral",r);if(a==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}}}else{var o=[n];while(n=e.parseElement("classRef",r)){o.push(n)}}if(r.matchToken("to")){var s=e.requireElement("expression",r)}else{var s=e.requireElement("implicitMeTarget",r)}if(r.matchToken("when")){if(a){e.raiseParseError(r,"Only class and properties are supported with a when clause")}var u=e.requireElement("expression",r)}if(o){return{classRefs:o,to:s,args:[s,o],op:function(e,r,n){t.nullCheck(r,s);t.forEach(n,(function(n){t.implicitLoop(r,(function(r){if(u){e.result=r;let i=t.evaluateNoPromise(u,e);if(i){if(r instanceof Element)r.classList.add(n.className)}else{if(r instanceof Element)r.classList.remove(n.className)}e.result=null}else{if(r instanceof Element)r.classList.add(n.className)}}))}));return t.findNext(this,e)}}}else if(i){return{type:"addCmd",attributeRef:i,to:s,args:[s],op:function(e,r,n){t.nullCheck(r,s);t.implicitLoop(r,(function(r){if(u){e.result=r;let n=t.evaluateNoPromise(u,e);if(n){r.setAttribute(i.name,i.value)}else{r.removeAttribute(i.name)}e.result=null}else{r.setAttribute(i.name,i.value)}}));return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}else{return{type:"addCmd",cssDeclaration:a,to:s,args:[s,a],op:function(e,r,n){t.nullCheck(r,s);t.implicitLoop(r,(function(e){e.style.cssText+=n}));return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}}}));e.addGrammarElement("styleLiteral",(function(e,t,r){if(!r.matchOpToken("{"))return;var n=[""];var i=[];while(r.hasMore()){if(r.matchOpToken("\\")){r.consumeToken()}else if(r.matchOpToken("}")){break}else if(r.matchToken("$")){var a=r.matchOpToken("{");var o=e.parseElement("expression",r);if(a)r.requireOpToken("}");i.push(o);n.push("")}else{var s=r.consumeToken();n[n.length-1]+=r.source.substring(s.start,s.end)}n[n.length-1]+=r.lastWhitespace()}return{type:"styleLiteral",args:[i],op:function(e,t){var r="";n.forEach((function(e,n){r+=e;if(n in t)r+=t[n]}));return r},evaluate:function(e){return t.unifiedEval(this,e)}}}));e.addCommand("remove",(function(e,t,r){if(r.matchToken("remove")){var n=e.parseElement("classRef",r);var i=null;var a=null;if(n==null){i=e.parseElement("attributeRef",r);if(i==null){a=e.parseElement("expression",r);if(a==null){e.raiseParseError(r,"Expected either a class reference, attribute expression or value expression")}}}else{var o=[n];while(n=e.parseElement("classRef",r)){o.push(n)}}if(r.matchToken("from")){var s=e.requireElement("expression",r)}else{if(a==null){var s=e.requireElement("implicitMeTarget",r)}}if(a){return{elementExpr:a,from:s,args:[a,s],op:function(e,r,n){t.nullCheck(r,a);t.implicitLoop(r,(function(e){if(e.parentElement&&(n==null||n.contains(e))){e.parentElement.removeChild(e)}}));return t.findNext(this,e)}}}else{return{classRefs:o,attributeRef:i,elementExpr:a,from:s,args:[o,s],op:function(e,r,n){t.nullCheck(n,s);if(r){t.forEach(r,(function(e){t.implicitLoop(n,(function(t){t.classList.remove(e.className)}))}))}else{t.implicitLoop(n,(function(e){e.removeAttribute(i.name)}))}return t.findNext(this,e)}}}}}));e.addCommand("toggle",(function(e,t,r){if(r.matchToken("toggle")){r.matchAnyToken("the","my");if(r.currentToken().type==="STYLE_REF"){let t=r.consumeToken();var n=t.value.substr(1);var a=true;var o=i(e,r,n);if(r.matchToken("of")){r.pushFollow("with");try{var s=e.requireElement("expression",r)}finally{r.popFollow()}}else{var s=e.requireElement("implicitMeTarget",r)}}else if(r.matchToken("between")){var u=true;var l=e.parseElement("classRef",r);r.requireToken("and");var c=e.requireElement("classRef",r)}else{var l=e.parseElement("classRef",r);var f=null;if(l==null){f=e.parseElement("attributeRef",r);if(f==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}}else{var m=[l];while(l=e.parseElement("classRef",r)){m.push(l)}}}if(a!==true){if(r.matchToken("on")){var s=e.requireElement("expression",r)}else{var s=e.requireElement("implicitMeTarget",r)}}if(r.matchToken("for")){var p=e.requireElement("expression",r)}else if(r.matchToken("until")){var h=e.requireElement("dotOrColonPath",r,"Expected event name");if(r.matchToken("from")){var v=e.requireElement("expression",r)}}var d={classRef:l,classRef2:c,classRefs:m,attributeRef:f,on:s,time:p,evt:h,from:v,toggle:function(e,r,n,i){t.nullCheck(e,s);if(a){t.implicitLoop(e,(function(e){o("toggle",e)}))}else if(u){t.implicitLoop(e,(function(e){if(e.classList.contains(r.className)){e.classList.remove(r.className);e.classList.add(n.className)}else{e.classList.add(r.className);e.classList.remove(n.className)}}))}else if(i){t.forEach(i,(function(r){t.implicitLoop(e,(function(e){e.classList.toggle(r.className)}))}))}else{t.forEach(e,(function(e){if(e.hasAttribute(f.name)){e.removeAttribute(f.name)}else{e.setAttribute(f.name,f.value)}}))}},args:[s,p,h,v,l,c,m],op:function(e,r,n,i,a,o,s,u){if(n){return new Promise((function(i){d.toggle(r,o,s,u);setTimeout((function(){d.toggle(r,o,s,u);i(t.findNext(d,e))}),n)}))}else if(i){return new Promise((function(n){var l=a||e.me;l.addEventListener(i,(function(){d.toggle(r,o,s,u);n(t.findNext(d,e))}),{once:true});d.toggle(r,o,s,u)}))}else{this.toggle(r,o,s,u);return t.findNext(d,e)}}};return d}}));var t={display:function(r,n,i){if(i){n.style.display=i}else if(r==="toggle"){if(getComputedStyle(n).display==="none"){t.display("show",n,i)}else{t.display("hide",n,i)}}else if(r==="hide"){const t=e.runtime.getInternalData(n);if(t.originalDisplay==null){t.originalDisplay=n.style.display}n.style.display="none"}else{const t=e.runtime.getInternalData(n);if(t.originalDisplay&&t.originalDisplay!=="none"){n.style.display=t.originalDisplay}else{n.style.removeProperty("display")}}},visibility:function(e,r,n){if(n){r.style.visibility=n}else if(e==="toggle"){if(getComputedStyle(r).visibility==="hidden"){t.visibility("show",r,n)}else{t.visibility("hide",r,n)}}else if(e==="hide"){r.style.visibility="hidden"}else{r.style.visibility="visible"}},opacity:function(e,r,n){if(n){r.style.opacity=n}else if(e==="toggle"){if(getComputedStyle(r).opacity==="0"){t.opacity("show",r,n)}else{t.opacity("hide",r,n)}}else if(e==="hide"){r.style.opacity="0"}else{r.style.opacity="1"}}};var n=function(e,t,r){var n;var i=r.currentToken();if(i.value==="when"||i.value==="with"||e.commandBoundary(i)){n=e.parseElement("implicitMeTarget",r)}else{n=e.parseElement("expression",r)}return n};var i=function(e,n,i){var a=r.defaultHideShowStrategy;var o=t;if(r.hideShowStrategies){o=Object.assign(o,r.hideShowStrategies)}i=i||a||"display";var s=o[i];if(s==null){e.raiseParseError(n,"Unknown show/hide strategy : "+i)}return s};e.addCommand("hide",(function(e,t,r){if(r.matchToken("hide")){var a=n(e,t,r);var o=null;if(r.matchToken("with")){o=r.requireTokenType("IDENTIFIER","STYLE_REF").value;if(o.indexOf("*")===0){o=o.substr(1)}}var s=i(e,r,o);return{target:a,args:[a],op:function(e,r){t.nullCheck(r,a);t.implicitLoop(r,(function(e){s("hide",e)}));return t.findNext(this,e)}}}}));e.addCommand("show",(function(e,t,r){if(r.matchToken("show")){var a=n(e,t,r);var o=null;if(r.matchToken("with")){o=r.requireTokenType("IDENTIFIER","STYLE_REF").value;if(o.indexOf("*")===0){o=o.substr(1)}}var s=null;if(r.matchOpToken(":")){var u=r.consumeUntilWhitespace();r.matchTokenType("WHITESPACE");s=u.map((function(e){return e.value})).join("")}if(r.matchToken("when")){var l=e.requireElement("expression",r)}var c=i(e,r,o);return{target:a,when:l,args:[a],op:function(e,r){t.nullCheck(r,a);t.implicitLoop(r,(function(r){if(l){e.result=r;let n=t.evaluateNoPromise(l,e);if(n){c("show",r,s)}else{c("hide",r)}e.result=null}else{c("show",r,s)}}));return t.findNext(this,e)}}}}));e.addCommand("take",(function(e,t,r){if(r.matchToken("take")){var n=e.parseElement("classRef",r);var i=null;var a=null;if(n==null){i=e.parseElement("attributeRef",r);if(i==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}if(r.matchToken("with")){a=e.requireElement("expression",r)}}if(r.matchToken("from")){var o=e.requireElement("expression",r)}else{var o=n}if(r.matchToken("for")){var s=e.requireElement("expression",r)}else{var s=e.requireElement("implicitMeTarget",r)}if(n){var u={classRef:n,from:o,forElt:s,args:[n,o,s],op:function(e,r,n,i){t.nullCheck(n,o);t.nullCheck(i,s);var a=r.className;t.implicitLoop(n,(function(e){e.classList.remove(a)}));t.implicitLoop(i,(function(e){e.classList.add(a)}));return t.findNext(this,e)}};return u}else{var u={attributeRef:i,from:o,forElt:s,args:[o,s,a],op:function(e,r,n,a){t.nullCheck(r,o);t.nullCheck(n,s);t.implicitLoop(r,(function(e){if(!a){e.removeAttribute(i.name)}else{e.setAttribute(i.name,a)}}));t.implicitLoop(n,(function(e){e.setAttribute(i.name,i.value||"")}));return t.findNext(this,e)}};return u}}}));function a(t,r,n,i){if(n!=null){var a=t.resolveSymbol(n,r)}else{var a=r}if(a instanceof Element||a instanceof HTMLDocument){while(a.firstChild)a.removeChild(a.firstChild);a.append(e.runtime.convertValue(i,"Fragment"));t.processNode(a)}else{if(n!=null){t.setSymbol(n,r,null,i)}else{throw"Don't know how to put a value into "+typeof r}}}e.addCommand("put",(function(e,t,r){if(r.matchToken("put")){var n=e.requireElement("expression",r);var i=r.matchAnyToken("into","before","after");if(i==null&&r.matchToken("at")){r.matchToken("the");i=r.matchAnyToken("start","end");r.requireToken("of")}if(i==null){e.raiseParseError(r,"Expected one of 'into', 'before', 'at start of', 'at end of', 'after'")}var o=e.requireElement("expression",r);var s=i.value;var u=false;var l=false;var c=null;var f=null;if(o.type==="arrayIndex"&&s==="into"){u=true;f=o.prop;c=o.root}else if(o.prop&&o.root&&s==="into"){f=o.prop.value;c=o.root}else if(o.type==="symbol"&&s==="into"){l=true;f=o.name}else if(o.type==="attributeRef"&&s==="into"){var m=true;f=o.name;c=e.requireElement("implicitMeTarget",r)}else if(o.type==="styleRef"&&s==="into"){var p=true;f=o.name;c=e.requireElement("implicitMeTarget",r)}else if(o.attribute&&s==="into"){var m=o.attribute.type==="attributeRef";var p=o.attribute.type==="styleRef";f=o.attribute.name;c=o.root}else{c=o}var h={target:o,operation:s,symbolWrite:l,value:n,args:[c,f,n],op:function(e,r,n,i){if(l){a(t,e,n,i)}else{t.nullCheck(r,c);if(s==="into"){if(m){t.implicitLoop(r,(function(e){e.setAttribute(n,i)}))}else if(p){t.implicitLoop(r,(function(e){e.style[n]=i}))}else if(u){r[n]=i}else{t.implicitLoop(r,(function(e){a(t,e,n,i)}))}}else{var o=s==="before"?Element.prototype.before:s==="after"?Element.prototype.after:s==="start"?Element.prototype.prepend:s==="end"?Element.prototype.append:Element.prototype.append;t.implicitLoop(r,(function(e){o.call(e,i instanceof Node?i:t.convertValue(i,"Fragment"));if(e.parentElement){t.processNode(e.parentElement)}else{t.processNode(e)}}))}}return t.findNext(this,e)}};return h}}));function o(e,t,r){var n;if(r.matchToken("the")||r.matchToken("element")||r.matchToken("elements")||r.currentToken().type==="CLASS_REF"||r.currentToken().type==="ID_REF"||r.currentToken().op&&r.currentToken().value==="<"){e.possessivesDisabled=true;try{n=e.parseElement("expression",r)}finally{delete e.possessivesDisabled}if(r.matchOpToken("'")){r.requireToken("s")}}else if(r.currentToken().type==="IDENTIFIER"&&r.currentToken().value==="its"){var i=r.matchToken("its");n={type:"pseudopossessiveIts",token:i,name:i.value,evaluate:function(e){return t.resolveSymbol("it",e)}}}else{r.matchToken("my")||r.matchToken("me");n=e.parseElement("implicitMeTarget",r)}return n}e.addCommand("transition",(function(e,t,n){if(n.matchToken("transition")){var i=o(e,t,n);var a=[];var s=[];var u=[];var l=n.currentToken();while(!e.commandBoundary(l)&&l.value!=="over"&&l.value!=="using"){if(n.currentToken().type==="STYLE_REF"){let e=n.consumeToken();let t=e.value.substr(1);a.push({type:"styleRefValue",evaluate:function(){return t}})}else{a.push(e.requireElement("stringLike",n))}if(n.matchToken("from")){s.push(e.requireElement("expression",n))}else{s.push(null)}n.requireToken("to");if(n.matchToken("initial")){u.push({type:"initial_literal",evaluate:function(){return"initial"}})}else{u.push(e.requireElement("expression",n))}l=n.currentToken()}if(n.matchToken("over")){var c=e.requireElement("expression",n)}else if(n.matchToken("using")){var f=e.requireElement("expression",n)}var m={to:u,args:[i,a,s,u,f,c],op:function(e,n,a,o,s,u,l){t.nullCheck(n,i);var c=[];t.implicitLoop(n,(function(e){var n=new Promise((function(n,i){var c=e.style.transition;if(l){e.style.transition="all "+l+"ms ease-in"}else if(u){e.style.transition=u}else{e.style.transition=r.defaultTransition}var f=t.getInternalData(e);var m=getComputedStyle(e);var p={};for(var h=0;he.forEach((e=>S(e))))).then((()=>n((function(){a();k.processNode(document.documentElement);e.document.addEventListener("htmx:load",(function(e){k.processNode(e.detail.elt)}))}))));function n(e){if(document.readyState!=="loading"){setTimeout(e)}else{document.addEventListener("DOMContentLoaded",e)}}function i(){var e=document.querySelector('meta[name="htmx-config"]');if(e){return v(e.content)}else{return null}}function a(){var e=i();if(e){Object.assign(r,e)}}}const S=Object.assign(b,{config:r,use(e){e(S)},internals:{lexer:x,parser:g,runtime:k,Lexer:n,Tokens:i,Parser:a,Runtime:o},ElementCollection:m,addFeature:g.addFeature.bind(g),addCommand:g.addCommand.bind(g),addLeafExpression:g.addLeafExpression.bind(g),addIndirectExpression:g.addIndirectExpression.bind(g),evaluate:k.evaluate.bind(k),parse:k.parse.bind(k),processNode:k.processNode.bind(k),browserInit:w});return S})); diff --git a/static/src/htmx.js b/static/src/htmx.js index 2fb8b9c..7b239be 100644 --- a/static/src/htmx.js +++ b/static/src/htmx.js @@ -1,3628 +1 @@ -// 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, - 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"], - }, - 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.3" - }; - - /** @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(", "); - - //==================================================================== - // Utilities - //==================================================================== - - function parseInterval(str) { - if (str == undefined) { - return undefined - } - 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 - } - if (str.slice(-1) == "m") { - return (parseFloat(str.slice(0,-1)) * 1000 * 60) || undefined - } - return parseFloat(str) || undefined - } - - /** - * @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 compatability - // 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 resp.match(/", 0); - // @ts-ignore type mismatch between DocumentFragment and Element. - // TODO: Are these close enough for htmx to use interchangably? - 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); - } - } - } - - /** - * @param {Function} func - */ - function maybeCall(func){ - if(func) { - func(); - } - } - - /** - * @param {any} o - * @param {string} type - * @returns - */ - function isType(o, type) { - return Object.prototype.toString.call(o) === "[object " + type + "]"; - } - - /** - * @param {*} o - * @returns {o is Function} - */ - function isFunction(o) { - return isType(o, "Function"); - } - - /** - * @param {*} o - * @returns {o is Object} - */ - function isRawObject(o) { - return isType(o, "Object"); - } - - /** - * getInternalData retrieves "private" data stored by htmx within an element - * @param {HTMLElement} elt - * @returns {*} - */ - function getInternalData(elt) { - var dataProp = 'htmx-internal-data'; - var data = elt[dataProp]; - if (!data) { - data = elt[dataProp] = {}; - } - return data; - } - - /** - * toArray converts an ArrayLike object into a real array. - * @param {ArrayLike} arr - * @returns {any[]} - */ - function toArray(arr) { - var returnArr = []; - if (arr) { - for (var i = 0; i < arr.length; i++) { - returnArr.push(arr[i]); - } - } - return returnArr - } - - function forEach(arr, func) { - if (arr) { - for (var i = 0; i < arr.length; i++) { - func(arr[i]); - } - } - } - - function isScrolledIntoView(el) { - var rect = el.getBoundingClientRect(); - var elemTop = rect.top; - var elemBottom = rect.bottom; - return elemTop < window.innerHeight && elemBottom >= 0; - } - - function bodyContains(elt) { - // IE Fix - if (elt.getRootNode && elt.getRootNode() instanceof ShadowRoot) { - return getDocument().body.contains(elt.getRootNode().host); - } else { - return getDocument().body.contains(elt); - } - } - - function splitOnWhitespace(trigger) { - return trigger.trim().split(/\s+/); - } - - /** - * mergeObjects takes all of the keys from - * obj2 and duplicates them into obj1 - * @param {Object} obj1 - * @param {Object} obj2 - * @returns {Object} - */ - function mergeObjects(obj1, obj2) { - for (var key in obj2) { - if (obj2.hasOwnProperty(key)) { - obj1[key] = obj2[key]; - } - } - return obj1; - } - - function parseJSON(jString) { - try { - return JSON.parse(jString); - } catch(error) { - logError(error); - return null; - } - } - - 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 (!path.match('^/$')) { - path = path.replace(/\/+$/, ''); - } - return path; - } catch (e) { - // be kind to IE11, which doesn't support URL() - return path; - } - } - - //========================================================================================== - // public API - //========================================================================================== - - function internalEval(str){ - return maybeEval(getDocument().body, function () { - return eval(str); - }); - } - - function onLoadHelper(callback) { - var value = htmx.on("htmx:load", function(evt) { - callback(evt.detail.elt); - }); - return value; - } - - function logAll(){ - htmx.logger = function(elt, event, data) { - if(console) { - console.log(event, elt, data); - } - } - } - - function logNone() { - htmx.logger = null - } - - function find(eltOrSelector, selector) { - if (selector) { - return eltOrSelector.querySelector(selector); - } else { - return find(getDocument(), eltOrSelector); - } - } - - function findAll(eltOrSelector, selector) { - if (selector) { - return eltOrSelector.querySelectorAll(selector); - } else { - return findAll(getDocument(), eltOrSelector); - } - } - - function removeElement(elt, delay) { - elt = resolveTarget(elt); - if (delay) { - setTimeout(function(){ - removeElement(elt); - elt = null; - }, delay); - } else { - elt.parentElement.removeChild(elt); - } - } - - function addClassToElement(elt, clazz, delay) { - elt = resolveTarget(elt); - if (delay) { - setTimeout(function(){ - addClassToElement(elt, clazz); - elt = null; - }, delay); - } else { - elt.classList && elt.classList.add(clazz); - } - } - - function removeClassFromElement(elt, clazz, delay) { - elt = resolveTarget(elt); - if (delay) { - setTimeout(function(){ - removeClassFromElement(elt, clazz); - elt = null; - }, delay); - } else { - if (elt.classList) { - elt.classList.remove(clazz); - // if there are no classes left, remove the class attribute - if (elt.classList.length === 0) { - elt.removeAttribute("class"); - } - } - } - } - - function toggleClassOnElement(elt, clazz) { - elt = resolveTarget(elt); - elt.classList.toggle(clazz); - } - - function takeClassForElement(elt, clazz) { - elt = resolveTarget(elt); - forEach(elt.parentElement.children, function(child){ - removeClassFromElement(child, clazz); - }) - addClassToElement(elt, clazz); - } - - function closest(elt, selector) { - elt = resolveTarget(elt); - 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 normalizeSelector(selector) { - var trimmedSelector = selector.trim(); - if (trimmedSelector.startsWith("<") && trimmedSelector.endsWith("/>")) { - return trimmedSelector.substring(1, trimmedSelector.length - 2); - } else { - return trimmedSelector; - } - } - - function querySelectorAllExt(elt, selector) { - if (selector.indexOf("closest ") === 0) { - return [closest(elt, normalizeSelector(selector.substr(8)))]; - } else if (selector.indexOf("find ") === 0) { - return [find(elt, normalizeSelector(selector.substr(5)))]; - } else if (selector.indexOf("next ") === 0) { - return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; - } 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 { - 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; - } - } - } - - function querySelectorExt(eltOrSelector, selector) { - if (selector) { - return querySelectorAllExt(eltOrSelector, selector)[0]; - } else { - return querySelectorAllExt(getDocument().body, eltOrSelector)[0]; - } - } - - function resolveTarget(arg2) { - if (isType(arg2, 'String')) { - return find(arg2); - } else { - return arg2; - } - } - - function processEventArgs(arg1, arg2, arg3) { - if (isFunction(arg2)) { - return { - target: getDocument().body, - event: arg1, - listener: arg2 - } - } else { - return { - target: resolveTarget(arg1), - event: arg2, - listener: arg3 - } - } - - } - - function addEventListenerImpl(arg1, arg2, arg3) { - ready(function(){ - var eventArgs = processEventArgs(arg1, arg2, arg3); - eventArgs.target.addEventListener(eventArgs.event, eventArgs.listener); - }) - var b = isFunction(arg2); - return b ? arg2 : arg3; - } - - function removeEventListenerImpl(arg1, arg2, arg3) { - ready(function(){ - var eventArgs = processEventArgs(arg1, arg2, arg3); - eventArgs.target.removeEventListener(eventArgs.event, eventArgs.listener); - }) - return isFunction(arg2) ? arg2 : arg3; - } - - //==================================================================== - // Node processing - //==================================================================== - - var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors - function findAttributeTargets(elt, attrName) { - var attrTarget = getClosestAttributeValue(elt, attrName); - if (attrTarget) { - if (attrTarget === "this") { - return [findThisElement(elt, attrName)]; - } else { - var result = querySelectorAllExt(elt, attrTarget); - if (result.length === 0) { - logError('The selector "' + attrTarget + '" on ' + attrName + " returned no matches!"); - return [DUMMY_ELT] - } else { - return result; - } - } - } - } - - function findThisElement(elt, attribute){ - return getClosestMatch(elt, function (elt) { - return getAttributeValue(elt, attribute) != null; - }) - } - - function getTarget(elt) { - var targetStr = getClosestAttributeValue(elt, "hx-target"); - if (targetStr) { - if (targetStr === "this") { - return findThisElement(elt,'hx-target'); - } else { - return querySelectorExt(elt, targetStr) - } - } else { - var data = getInternalData(elt); - if (data.boosted) { - return getDocument().body; - } else { - return elt; - } - } - } - - function shouldSettleAttribute(name) { - var attributesToSettle = htmx.config.attributesToSettle; - for (var i = 0; i < attributesToSettle.length; i++) { - if (name === attributesToSettle[i]) { - return true; - } - } - return false; - } - - function cloneAttributes(mergeTo, mergeFrom) { - forEach(mergeTo.attributes, function (attr) { - if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) { - mergeTo.removeAttribute(attr.name) - } - }); - forEach(mergeFrom.attributes, function (attr) { - if (shouldSettleAttribute(attr.name)) { - mergeTo.setAttribute(attr.name, attr.value); - } - }); - } - - function isInlineSwap(swapStyle, target) { - var extensions = getExtensions(target); - for (var i = 0; i < extensions.length; i++) { - var extension = extensions[i]; - try { - if (extension.isInlineSwap(swapStyle)) { - return true; - } - } catch(e) { - logError(e); - } - } - return swapStyle === "outerHTML"; - } - - /** - * - * @param {string} oobValue - * @param {HTMLElement} oobElement - * @param {*} settleInfo - * @returns - */ - function oobSwap(oobValue, oobElement, settleInfo) { - var selector = "#" + oobElement.id; - var swapStyle = "outerHTML"; - if (oobValue === "true") { - // do nothing - } else if (oobValue.indexOf(":") > 0) { - swapStyle = oobValue.substr(0, oobValue.indexOf(":")); - selector = oobValue.substr(oobValue.indexOf(":") + 1, oobValue.length); - } else { - swapStyle = oobValue; - } - - var targets = getDocument().querySelectorAll(selector); - if (targets) { - forEach( - targets, - function (target) { - var fragment; - var oobElementClone = oobElement.cloneNode(true); - fragment = getDocument().createDocumentFragment(); - fragment.appendChild(oobElementClone); - if (!isInlineSwap(swapStyle, target)) { - fragment = oobElementClone; // if this is not an inline swap, we use the content of the node, not the node itself - } - - var beforeSwapDetails = {shouldSwap: true, target: target, fragment:fragment }; - if (!triggerEvent(target, 'htmx:oobBeforeSwap', beforeSwapDetails)) return; - - target = beforeSwapDetails.target; // allow re-targeting - if (beforeSwapDetails['shouldSwap']){ - swap(swapStyle, target, target, fragment, settleInfo); - } - forEach(settleInfo.elts, function (elt) { - triggerEvent(elt, 'htmx:oobAfterSwap', beforeSwapDetails); - }); - } - ); - oobElement.parentNode.removeChild(oobElement); - } else { - oobElement.parentNode.removeChild(oobElement); - triggerErrorEvent(getDocument().body, "htmx:oobErrorNoTarget", {content: oobElement}); - } - return oobValue; - } - - function handleOutOfBandSwaps(elt, fragment, settleInfo) { - var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); - if (oobSelects) { - var oobSelectValues = oobSelects.split(","); - for (let 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) { - oobSwap(oobValue, oobElement, settleInfo); - } - }); - } - - function handlePreservedElements(fragment) { - forEach(findAll(fragment, '[hx-preserve], [data-hx-preserve]'), function (preservedElt) { - var id = getAttributeValue(preservedElt, "id"); - var oldElt = getDocument().getElementById(id); - if (oldElt != null) { - preservedElt.parentNode.replaceChild(oldElt, preservedElt); - } - }); - } - - function handleAttributes(parentNode, fragment, settleInfo) { - forEach(fragment.querySelectorAll("[id]"), function (newNode) { - if (newNode.id && newNode.id.length > 0) { - var normalizedId = newNode.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); - settleInfo.tasks.push(function () { - cloneAttributes(newNode, newAttributes); - }); - } - } - }); - } - - function makeAjaxLoadTask(child) { - return function () { - removeClassFromElement(child, htmx.config.addedClass); - processNode(child); - processScripts(child); - processFocus(child) - triggerEvent(child, 'htmx:load'); - }; - } - - function processFocus(child) { - var autofocus = "[autofocus]"; - var autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus) - if (autoFocusedElt != null) { - autoFocusedElt.focus(); - } - } - - function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) { - handleAttributes(parentNode, fragment, settleInfo); - while(fragment.childNodes.length > 0){ - var child = fragment.firstChild; - addClassToElement(child, htmx.config.addedClass); - parentNode.insertBefore(child, insertBefore); - if (child.nodeType !== Node.TEXT_NODE && child.nodeType !== Node.COMMENT_NODE) { - settleInfo.tasks.push(makeAjaxLoadTask(child)); - } - } - } - - // 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 (let i = 0; i < internalData.onHandlers.length; i++) { - const handlerInfo = internalData.onHandlers[i]; - elt.removeEventListener(handlerInfo.name, handlerInfo.handler); - } - 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(); - } - if (internalData.listenerInfos) { - forEach(internalData.listenerInfos, function (info) { - if (info.on) { - info.on.removeEventListener(info.trigger, info.listener); - } - }); - } - deInitOnHandlers(element); - } - - function cleanUpElement(element) { - triggerEvent(element, "htmx:beforeCleanupElement") - deInitNode(element); - if (element.children) { // IE - forEach(element.children, function(child) { cleanUpElement(child) }); - } - } - - function swapOuterHTML(target, fragment, settleInfo) { - if (target.tagName === "BODY") { - return swapInnerHTML(target, fragment, settleInfo); - } else { - // @type {HTMLElement} - var newElt - var eltBeforeNewContent = target.previousSibling; - insertNodesBefore(parentElt(target), target, fragment, settleInfo); - if (eltBeforeNewContent == null) { - newElt = parentElt(target).firstChild; - } else { - newElt = eltBeforeNewContent.nextSibling; - } - getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later - settleInfo.elts = settleInfo.elts.filter(e => e != target); - while(newElt && newElt !== target) { - if (newElt.nodeType === Node.ELEMENT_NODE) { - settleInfo.elts.push(newElt); - } - newElt = newElt.nextElementSibling; - } - cleanUpElement(target); - parentElt(target).removeChild(target); - } - } - - function swapAfterBegin(target, fragment, settleInfo) { - return insertNodesBefore(target, target.firstChild, fragment, settleInfo); - } - - function swapBeforeBegin(target, fragment, settleInfo) { - return insertNodesBefore(parentElt(target), target, fragment, settleInfo); - } - - function swapBeforeEnd(target, fragment, settleInfo) { - return insertNodesBefore(target, null, fragment, settleInfo); - } - - function swapAfterEnd(target, fragment, settleInfo) { - return insertNodesBefore(parentElt(target), target.nextSibling, fragment, settleInfo); - } - function swapDelete(target, fragment, settleInfo) { - cleanUpElement(target); - return parentElt(target).removeChild(target); - } - - function swapInnerHTML(target, fragment, settleInfo) { - var firstChild = target.firstChild; - insertNodesBefore(target, firstChild, fragment, settleInfo); - if (firstChild) { - while (firstChild.nextSibling) { - cleanUpElement(firstChild.nextSibling) - target.removeChild(firstChild.nextSibling); - } - cleanUpElement(firstChild) - target.removeChild(firstChild); - } - } - - function maybeSelectFromResponse(elt, fragment, selectOverride) { - var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); - if (selector) { - var newFragment = getDocument().createDocumentFragment(); - forEach(fragment.querySelectorAll(selector), function (node) { - newFragment.appendChild(node); - }); - fragment = newFragment; - } - return fragment; - } - - function swap(swapStyle, elt, target, fragment, settleInfo) { - switch (swapStyle) { - case "none": - return; - case "outerHTML": - swapOuterHTML(target, fragment, settleInfo); - return; - case "afterbegin": - swapAfterBegin(target, fragment, settleInfo); - return; - case "beforebegin": - swapBeforeBegin(target, fragment, settleInfo); - return; - case "beforeend": - swapBeforeEnd(target, fragment, settleInfo); - return; - case "afterend": - swapAfterEnd(target, fragment, settleInfo); - return; - case "delete": - swapDelete(target, fragment, settleInfo); - return; - default: - var extensions = getExtensions(elt); - for (var i = 0; i < extensions.length; i++) { - var ext = extensions[i]; - try { - var newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo); - if (newElements) { - if (typeof newElements.length !== 'undefined') { - // if handleSwap returns an array (like) of elements, we handle them - for (var j = 0; j < newElements.length; j++) { - var child = newElements[j]; - if (child.nodeType !== Node.TEXT_NODE && child.nodeType !== Node.COMMENT_NODE) { - settleInfo.tasks.push(makeAjaxLoadTask(child)); - } - } - } - return; - } - } catch (e) { - logError(e); - } - } - if (swapStyle === "innerHTML") { - swapInnerHTML(target, fragment, settleInfo); - } else { - swap(htmx.config.defaultSwapStyle, elt, target, fragment, settleInfo); - } - } - } - - function findTitle(content) { - if (content.indexOf(' -1) { - var contentWithSvgsRemoved = content.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - var result = contentWithSvgsRemoved.match(/]*>|>)([\s\S]*?)<\/title>/im); - - if (result) { - return result[2]; - } - } - } - - function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { - settleInfo.title = findTitle(responseText); - var fragment = makeFragment(responseText); - if (fragment) { - handleOutOfBandSwaps(elt, fragment, settleInfo); - fragment = maybeSelectFromResponse(elt, fragment, selectOverride); - handlePreservedElements(fragment); - return swap(swapStyle, elt, target, fragment, settleInfo); - } - } - - function handleTrigger(xhr, header, elt) { - var triggerBody = xhr.getResponseHeader(header); - if (triggerBody.indexOf("{") === 0) { - var triggers = parseJSON(triggerBody); - for (var eventName in triggers) { - if (triggers.hasOwnProperty(eventName)) { - var detail = triggers[eventName]; - if (!isRawObject(detail)) { - detail = {"value": detail} - } - triggerEvent(elt, eventName, detail); - } - } - } else { - triggerEvent(elt, triggerBody, []); - } - } - - var WHITESPACE = /\s/; - var WHITESPACE_OR_COMMA = /[\s,]/; - var SYMBOL_START = /[_$a-zA-Z]/; - var SYMBOL_CONT = /[_$a-zA-Z0-9]/; - var STRINGISH_START = ['"', "'", "/"]; - var NOT_WHITESPACE = /[^\s]/; - function tokenizeString(str) { - var tokens = []; - var position = 0; - while (position < str.length) { - if(SYMBOL_START.exec(str.charAt(position))) { - var startPosition = position; - while (SYMBOL_CONT.exec(str.charAt(position + 1))) { - position++; - } - tokens.push(str.substr(startPosition, position - startPosition + 1)); - } else if (STRINGISH_START.indexOf(str.charAt(position)) !== -1) { - var startChar = str.charAt(position); - var startPosition = position; - position++; - while (position < str.length && str.charAt(position) !== startChar ) { - if (str.charAt(position) === "\\") { - position++; - } - position++; - } - tokens.push(str.substr(startPosition, position - startPosition + 1)); - } else { - var symbol = str.charAt(position); - tokens.push(symbol); - } - position++; - } - return tokens; - } - - function isPossibleRelativeReference(token, last, paramName) { - return SYMBOL_START.exec(token.charAt(0)) && - token !== "true" && - token !== "false" && - token !== "this" && - token !== paramName && - last !== "."; - } - - function maybeGenerateConditional(elt, tokens, paramName) { - if (tokens[0] === '[') { - tokens.shift(); - var bracketCount = 1; - var conditionalSource = " return (function(" + paramName + "){ return ("; - var last = null; - while (tokens.length > 0) { - var token = tokens[0]; - if (token === "]") { - bracketCount--; - if (bracketCount === 0) { - if (last === null) { - conditionalSource = conditionalSource + "true"; - } - tokens.shift(); - conditionalSource += ")})"; - try { - var conditionFunction = maybeEval(elt,function () { - return Function(conditionalSource)(); - }, - function(){return true}) - conditionFunction.source = conditionalSource; - return conditionFunction; - } catch (e) { - triggerErrorEvent(getDocument().body, "htmx:syntax:error", {error:e, source:conditionalSource}) - return null; - } - } - } else if (token === "[") { - bracketCount++; - } - if (isPossibleRelativeReference(token, last, paramName)) { - conditionalSource += "((" + paramName + "." + token + ") ? (" + paramName + "." + token + ") : (window." + token + "))"; - } else { - conditionalSource = conditionalSource + token; - } - last = tokens.shift(); - } - } - } - - function consumeUntil(tokens, match) { - var result = ""; - while (tokens.length > 0 && !tokens[0].match(match)) { - result += tokens.shift(); - } - return result; - } - - var INPUT_SELECTOR = 'input, textarea, select'; - - /** - * @param {HTMLElement} elt - * @returns {import("./htmx").HtmxTriggerSpecification[]} - */ - function getTriggerSpecs(elt) { - var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); - 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 from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); - if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { - tokens.shift(); - from_arg += - " " + - consumeUntil( - tokens, - WHITESPACE_OR_COMMA - ); - } - 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()}); - } - } - 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 (triggerSpecs.length > 0) { - return triggerSpecs; - } else if (matches(elt, 'form')) { - return [{trigger: 'submit'}]; - } else if (matches(elt, 'input[type="button"]')){ - return [{trigger: 'click'}]; - } else if (matches(elt, INPUT_SELECTOR)) { - return [{trigger: 'change'}]; - } else { - return [{trigger: 'click'}]; - } - } - - function cancelPolling(elt) { - getInternalData(elt).cancelled = true; - } - - function processPolling(elt, handler, spec) { - var nodeData = getInternalData(elt); - nodeData.timeout = setTimeout(function () { - if (bodyContains(elt) && nodeData.cancelled !== true) { - if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { - triggerSpec: spec, - target: elt - }))) { - handler(elt); - } - processPolling(elt, handler, spec); - } - }, spec.pollInterval); - } - - function isLocalLink(elt) { - return location.hostname === elt.hostname && - getRawAttribute(elt,'href') && - getRawAttribute(elt,'href').indexOf("#") !== 0; - } - - function boostElement(elt, nodeData, triggerSpecs) { - 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 = elt.href; // DOM property gives the fully resolved href of a relative link - } else { - var rawAttribute = getRawAttribute(elt, "method"); - verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; - if (verb === "get") { - } - path = getRawAttribute(elt, 'action'); - } - triggerSpecs.forEach(function(triggerSpec) { - addEventListener(elt, function(elt, evt) { - issueAjaxRequest(verb, path, elt, evt) - }, nodeData, triggerSpec, true); - }); - } - } - - /** - * - * @param {Event} evt - * @param {HTMLElement} elt - * @returns - */ - function shouldCancel(evt, elt) { - if (evt.type === "submit" || evt.type === "click") { - if (elt.tagName === "FORM") { - return true; - } - if (matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) { - return true; - } - if (elt.tagName === "A" && elt.href && - (elt.getAttribute('href') === '#' || elt.getAttribute('href').indexOf("#") !== 0)) { - return true; - } - } - return false; - } - - function ignoreBoostedAnchorCtrlClick(elt, evt) { - return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); - } - - function maybeFilterEvent(triggerSpec, elt, evt) { - var eventFilter = triggerSpec.eventFilter; - if(eventFilter){ - try { - return eventFilter.call(elt, evt) !== true; - } catch(e) { - triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", {error: e, source:eventFilter.source}); - return true; - } - } - return false; - } - - 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 value of the element so we can tell if it changes - if (triggerSpec.changed) { - elementData.lastValue = elt.value; - } - forEach(eltsToListenOn, function (eltToListenOn) { - var eventListener = function (evt) { - if (!bodyContains(elt)) { - eltToListenOn.removeEventListener(triggerSpec.trigger, eventListener); - return; - } - if (ignoreBoostedAnchorCtrlClick(elt, evt)) { - return; - } - if (explicitCancel || shouldCancel(evt, elt)) { - evt.preventDefault(); - } - if (maybeFilterEvent(triggerSpec, elt, evt)) { - return; - } - var eventData = getInternalData(evt); - eventData.triggerSpec = triggerSpec; - if (eventData.handledFor == null) { - eventData.handledFor = []; - } - if (eventData.handledFor.indexOf(elt) < 0) { - eventData.handledFor.push(elt); - if (triggerSpec.consume) { - evt.stopPropagation(); - } - if (triggerSpec.target && evt.target) { - if (!matches(evt.target, triggerSpec.target)) { - return; - } - } - if (triggerSpec.once) { - if (elementData.triggeredOnce) { - return; - } else { - elementData.triggeredOnce = true; - } - } - if (triggerSpec.changed) { - if (elementData.lastValue === elt.value) { - return; - } else { - elementData.lastValue = elt.value; - } - } - if (elementData.delayed) { - clearTimeout(elementData.delayed); - } - if (elementData.throttle) { - return; - } - - if (triggerSpec.throttle) { - if (!elementData.throttle) { - handler(elt, evt); - elementData.throttle = setTimeout(function () { - elementData.throttle = null; - }, triggerSpec.throttle); - } - } else if (triggerSpec.delay) { - elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay); - } else { - triggerEvent(elt, 'htmx:trigger') - handler(elt, evt); - } - } - }; - if (nodeData.listenerInfos == null) { - nodeData.listenerInfos = []; - } - nodeData.listenerInfos.push({ - trigger: triggerSpec.trigger, - listener: eventListener, - on: eltToListenOn - }) - eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); - }); - } - - var windowIsScrolling = false // used by initScrollHandler - var scrollHandler = null; - function initScrollHandler() { - if (!scrollHandler) { - scrollHandler = function() { - windowIsScrolling = true - }; - window.addEventListener("scroll", scrollHandler) - setInterval(function() { - if (windowIsScrolling) { - windowIsScrolling = false; - forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) { - maybeReveal(elt); - }) - } - }, 200); - } - } - - function maybeReveal(elt) { - if (!hasAttribute(elt,'data-hx-revealed') && isScrolledIntoView(elt)) { - elt.setAttribute('data-hx-revealed', 'true'); - var nodeData = getInternalData(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(evt) { triggerEvent(elt, 'revealed') }, {once: true}); - } - } - } - - //==================================================================== - // Web Sockets - //==================================================================== - - function processWebSocketInfo(elt, nodeData, info) { - var values = splitOnWhitespace(info); - for (var i = 0; i < values.length; i++) { - var value = values[i].split(/:(.+)/); - if (value[0] === "connect") { - ensureWebSocket(elt, value[1], 0); - } - if (value[0] === "send") { - processWebSocketSend(elt); - } - } - } - - function ensureWebSocket(elt, wssSource, retryCount) { - if (!bodyContains(elt)) { - return; // stop ensuring websocket connection when socket bearing element ceases to exist - } - - if (wssSource.indexOf("/") == 0) { // complete absolute paths only - var base_part = location.hostname + (location.port ? ':'+location.port: ''); - if (location.protocol == 'https:') { - wssSource = "wss://" + base_part + wssSource; - } else if (location.protocol == 'http:') { - wssSource = "ws://" + base_part + wssSource; - } - } - var socket = htmx.createWebSocket(wssSource); - socket.onerror = function (e) { - triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket}); - maybeCloseWebSocketSource(elt); - }; - - socket.onclose = function (e) { - if ([1006, 1012, 1013].indexOf(e.code) >= 0) { // Abnormal Closure/Service Restart/Try Again Later - var delay = getWebSocketReconnectDelay(retryCount); - setTimeout(function() { - ensureWebSocket(elt, wssSource, retryCount+1); // creates a websocket with a new timeout - }, delay); - } - }; - socket.onopen = function (e) { - retryCount = 0; - } - - getInternalData(elt).webSocket = socket; - socket.addEventListener('message', function (event) { - if (maybeCloseWebSocketSource(elt)) { - return; - } - - var response = event.data; - withExtensions(elt, function(extension){ - response = extension.transformResponse(response, null, elt); - }); - - var settleInfo = makeSettleInfo(elt); - var fragment = makeFragment(response); - var children = toArray(fragment.children); - for (var i = 0; i < children.length; i++) { - var child = children[i]; - oobSwap(getAttributeValue(child, "hx-swap-oob") || "true", child, settleInfo); - } - - settleImmediately(settleInfo.tasks); - }); - } - - function maybeCloseWebSocketSource(elt) { - if (!bodyContains(elt)) { - getInternalData(elt).webSocket.close(); - return true; - } - } - - function processWebSocketSend(elt) { - var webSocketSourceElt = getClosestMatch(elt, function (parent) { - return getInternalData(parent).webSocket != null; - }); - if (webSocketSourceElt) { - elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) { - var webSocket = getInternalData(webSocketSourceElt).webSocket; - var headers = getHeaders(elt, webSocketSourceElt); - var results = getInputValues(elt, 'post'); - var errors = results.errors; - var rawParameters = results.values; - var expressionVars = getExpressionVars(elt); - var allParameters = mergeObjects(rawParameters, expressionVars); - var filteredParameters = filterValues(allParameters, elt); - filteredParameters['HEADERS'] = headers; - if (errors && errors.length > 0) { - triggerEvent(elt, 'htmx:validation:halted', errors); - return; - } - webSocket.send(JSON.stringify(filteredParameters)); - if(shouldCancel(evt, elt)){ - evt.preventDefault(); - } - }); - } else { - triggerErrorEvent(elt, "htmx:noWebSocketSourceError"); - } - } - - function getWebSocketReconnectDelay(retryCount) { - var delay = htmx.config.wsReconnectDelay; - if (typeof delay === 'function') { - // @ts-ignore - return delay(retryCount); - } - if (delay === 'full-jitter') { - var exp = Math.min(retryCount, 6); - var maxDelay = 1000 * Math.pow(2, exp); - return maxDelay * Math.random(); - } - logError('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"'); - } - - //==================================================================== - // Server Sent Events - //==================================================================== - - function processSSEInfo(elt, nodeData, info) { - var values = splitOnWhitespace(info); - for (var i = 0; i < values.length; i++) { - var value = values[i].split(/:(.+)/); - if (value[0] === "connect") { - processSSESource(elt, value[1]); - } - - if ((value[0] === "swap")) { - processSSESwap(elt, value[1]) - } - } - } - - function processSSESource(elt, sseSrc) { - var source = htmx.createEventSource(sseSrc); - source.onerror = function (e) { - triggerErrorEvent(elt, "htmx:sseError", {error:e, source:source}); - maybeCloseSSESource(elt); - }; - getInternalData(elt).sseEventSource = source; - } - - function processSSESwap(elt, sseEventName) { - var sseSourceElt = getClosestMatch(elt, hasEventSource); - if (sseSourceElt) { - var sseEventSource = getInternalData(sseSourceElt).sseEventSource; - var sseListener = function (event) { - if (maybeCloseSSESource(sseSourceElt)) { - return; - } - if (!bodyContains(elt)) { - sseEventSource.removeEventListener(sseEventName, sseListener); - return; - } - - /////////////////////////// - // TODO: merge this code with AJAX and WebSockets code in the future. - - var response = event.data; - withExtensions(elt, function(extension){ - response = extension.transformResponse(response, null, elt); - }); - - var swapSpec = getSwapSpecification(elt) - var target = getTarget(elt) - var settleInfo = makeSettleInfo(elt); - - selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) - settleImmediately(settleInfo.tasks) - triggerEvent(elt, "htmx:sseMessage", event) - }; - - getInternalData(elt).sseListener = sseListener; - sseEventSource.addEventListener(sseEventName, sseListener); - } else { - triggerErrorEvent(elt, "htmx:noSSESourceError"); - } - } - - 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)) { - handler(elt); - } else { - sseEventSource.removeEventListener(sseEventName, sseListener); - } - } - }; - getInternalData(elt).sseListener = sseListener; - sseEventSource.addEventListener(sseEventName, sseListener); - } else { - triggerErrorEvent(elt, "htmx:noSSESourceError"); - } - } - - function maybeCloseSSESource(elt) { - if (!bodyContains(elt)) { - getInternalData(elt).sseEventSource.close(); - return true; - } - } - - function hasEventSource(node) { - return getInternalData(node).sseEventSource != null; - } - - //==================================================================== - - function loadImmediately(elt, handler, nodeData, delay) { - var load = function(){ - if (!nodeData.loaded) { - nodeData.loaded = true; - handler(elt); - } - } - if (delay) { - setTimeout(load, delay); - } else { - load(); - } - } - - function processVerbs(elt, nodeData, triggerSpecs) { - var explicitAction = false; - forEach(VERBS, function (verb) { - if (hasAttribute(elt,'hx-' + verb)) { - var path = getAttributeValue(elt, 'hx-' + verb); - explicitAction = true; - nodeData.path = path; - nodeData.verb = verb; - triggerSpecs.forEach(function(triggerSpec) { - addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { - 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) { - 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 === "") { - var 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; - } - var parent = script.parentElement; - - try { - parent.insertBefore(newScript, script); - } catch (e) { - logError(e); - } finally { - // remove old script element, but only if it is still in DOM - if (script.parentElement) { - script.parentElement.removeChild(script); - } - } - } - } - - function processScripts(elt) { - if (matches(elt, "script")) { - evalScript(elt); - } - forEach(findAll(elt, "script"), function (script) { - evalScript(script); - }); - } - - function hasChanceOfBeingBoosted() { - return document.querySelector("[hx-boost], [data-hx-boost]"); - } - - function findHxOnWildcardElements(elt) { - if (!document.evaluate) return [] - - let node = null - const elements = [] - const iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt) - while (node = iter.iterateNext()) elements.push(node) - 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], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); - return results; - } else { - return []; - } - } - - function initButtonTracking(form){ - var maybeSetLastButtonClicked = function(evt){ - var elt = closest(evt.target, "button, input[type='submit']"); - if (elt !== null) { - var internalData = getInternalData(form); - internalData.lastButtonClicked = 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 - - 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 (let 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); - nodeData.onHandlers = []; - var func = new Function("event", code + "; return;"); - var listener = elt.addEventListener(eventName, function (e) { - return func.call(elt, e); - }); - nodeData.onHandlers.push({event:eventName, listener:listener}); - return {nodeData, code, func, listener}; - } - - function processHxOn(elt) { - var hxOnValue = getAttributeValue(elt, 'hx-on'); - if (hxOnValue && htmx.config.allowEval) { - 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 (const attr of elt.attributes) { - const { name, value } = attr - if (name.startsWith("hx-on:") || name.startsWith("data-hx-on:")) { - let eventName = name.slice(name.indexOf(":") + 1) - // if the eventName starts with a colon, prepend "htmx" for shorthand support - if (eventName.startsWith(":")) eventName = "htmx" + eventName - - addHxOnEventHandler(elt, eventName, value) - } - } - } - - function initNode(elt) { - if (elt.closest && elt.closest(htmx.config.disableSelector)) { - return; - } - var nodeData = getInternalData(elt); - if (nodeData.initHash !== attributeHash(elt)) { - - nodeData.initHash = attributeHash(elt); - - // clean up any previously processed info - deInitNode(elt); - - processHxOn(elt); - - triggerEvent(elt, "htmx:beforeProcessNode") - - if (elt.value) { - nodeData.lastValue = elt.value; - } - - var triggerSpecs = getTriggerSpecs(elt); - 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); - } - - var sseInfo = getAttributeValue(elt, 'hx-sse'); - if (sseInfo) { - processSSEInfo(elt, nodeData, sseInfo); - } - - var wsInfo = getAttributeValue(elt, 'hx-ws'); - if (wsInfo) { - processWebSocketInfo(elt, nodeData, wsInfo); - } - triggerEvent(elt, "htmx:afterProcessNode"); - } - } - - function processNode(elt) { - elt = resolveTarget(elt); - 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); - } - - //==================================================================== - // Event/Log Support - //==================================================================== - - function kebabEventName(str) { - return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); - } - - function makeEvent(eventName, detail) { - var evt; - if (window.CustomEvent && typeof window.CustomEvent === 'function') { - evt = new CustomEvent(eventName, {bubbles: true, cancelable: true, detail: detail}); - } else { - evt = getDocument().createEvent('CustomEvent'); - evt.initCustomEvent(eventName, true, true, detail); - } - return evt; - } - - function triggerErrorEvent(elt, eventName, detail) { - triggerEvent(elt, eventName, mergeObjects({error:eventName}, detail)); - } - - function ignoreEventForLogging(eventName) { - return eventName === "htmx:afterProcessNode" - } - - /** - * `withExtensions` locates all active extensions for a provided element, then - * executes the provided function using each of the active extensions. It should - * be called internally at every extendable execution point in htmx. - * - * @param {HTMLElement} elt - * @param {(extension:import("./htmx").HtmxExtension) => void} toDo - * @returns void - */ - function withExtensions(elt, toDo) { - forEach(getExtensions(elt), function(extension){ - try { - toDo(extension); - } catch (e) { - logError(e); - } - }); - } - - function logError(msg) { - if(console.error) { - console.error(msg); - } else if (console.log) { - console.log("ERROR: ", msg); - } - } - - function triggerEvent(elt, eventName, detail) { - elt = resolveTarget(elt); - if (detail == null) { - detail = {}; - } - detail["elt"] = elt; - var event = makeEvent(eventName, detail); - if (htmx.logger && !ignoreEventForLogging(eventName)) { - htmx.logger(elt, eventName, detail); - } - if (detail.error) { - logError(detail.error); - triggerEvent(elt, "htmx:error", {errorInfo:detail}) - } - var eventResult = elt.dispatchEvent(event); - var kebabName = kebabEventName(eventName); - if (eventResult && kebabName !== eventName) { - var kebabedEvent = makeEvent(kebabName, event.detail); - eventResult = eventResult && elt.dispatchEvent(kebabedEvent) - } - withExtensions(elt, function (extension) { - eventResult = eventResult && (extension.onEvent(eventName, event) !== false) - }); - return eventResult; - } - - //==================================================================== - // History Support - //==================================================================== - var currentPathForHistory = location.pathname+location.search; - - function getHistoryElement() { - var historyElt = getDocument().querySelector('[hx-history-elt],[data-hx-history-elt]'); - return historyElt || getDocument().body; - } - - function saveToHistoryCache(url, content, title, scroll) { - if (!canAccessLocalStorage()) { - 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) { - historyCache.splice(i, 1); - break; - } - } - 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(); - } - while(historyCache.length > 0){ - try { - localStorage.setItem("htmx-history-cache", JSON.stringify(historyCache)); - break; - } catch (e) { - triggerErrorEvent(getDocument().body, "htmx:historyCacheError", {cause:e, cache: historyCache}) - historyCache.shift(); // shrink the cache and retry - } - } - } - - 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) { - return historyCache[i]; - } - } - return null; - } - - function cleanInnerHtmlForHistory(elt) { - var className = htmx.config.requestClass; - var clone = elt.cloneNode(true); - forEach(findAll(clone, "." + className), function(child){ - removeClassFromElement(child, className); - }); - return clone.innerHTML; - } - - function saveCurrentPageToHistory() { - var elt = getHistoryElement(); - var path = currentPathForHistory || location.pathname+location.search; - - // 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 = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]'); - 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) { - // remove the cache buster parameter, if any - if (htmx.config.getCacheBusterParam) { - path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') - if (path.endsWith('&') || path.endsWith("?")) { - 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; - } - - function settleImmediately(tasks) { - forEach(tasks, function (task) { - task.call(); - }); - } - - function loadHistoryFromServer(path) { - var request = new XMLHttpRequest(); - var details = {path: path, xhr:request}; - triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); - request.open('GET', path, true); - request.setRequestHeader("HX-History-Restore-Request", "true"); - request.onload = function () { - if (this.status >= 200 && this.status < 400) { - triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); - var fragment = makeFragment(this.response); - // @ts-ignore - 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, cacheMiss:true, serverResponse:this.response}); - } else { - triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); - } - }; - request.send(); - } - - function restoreHistory(path) { - saveCurrentPageToHistory(); - path = path || location.pathname+location.search; - var cached = getCachedHistory(path); - if (cached) { - var fragment = makeFragment(cached.content); - var historyElement = getHistoryElement(); - var settleInfo = makeSettleInfo(historyElement); - swapInnerHTML(historyElement, fragment, settleInfo) - settleImmediately(settleInfo.tasks); - document.title = cached.title; - 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, item:cached}); - } else { - if (htmx.config.refreshOnHistoryMiss) { - - // @ts-ignore: optional parameter in reload() function throws error - window.location.reload(true); - } else { - loadHistoryFromServer(path); - } - } - } - - 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) { - forEach(indicators, function (ic) { - var internalData = getInternalData(ic); - internalData.requestCount = (internalData.requestCount || 0) - 1; - if (internalData.requestCount === 0) { - ic.classList["remove"].call(ic.classList, htmx.config.requestClass); - } - }); - } - - //==================================================================== - // Input Value Processing - //==================================================================== - - function haveSeenNode(processed, elt) { - for (var i = 0; i < processed.length; i++) { - var node = processed[i]; - if (node.isSameNode(elt)) { - return true; - } - } - return false; - } - - function shouldInclude(elt) { - if(elt.name === "" || elt.name == null || elt.disabled) { - return false; - } - // ignore "submitter" types (see jQuery src/serialize.js) - if (elt.type === "button" || elt.type === "submit" || elt.tagName === "image" || elt.tagName === "reset" || elt.tagName === "file" ) { - return false; - } - if (elt.type === "checkbox" || elt.type === "radio" ) { - return elt.checked; - } - return true; - } - - function processInputValue(processed, values, errors, elt, validate) { - if (elt == null || haveSeenNode(processed, elt)) { - return; - } else { - processed.push(elt); - } - if (shouldInclude(elt)) { - var name = getRawAttribute(elt,"name"); - var value = elt.value; - if (elt.multiple) { - 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 !== undefined) { - 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; - } - } - if (validate) { - validateElement(elt, errors); - } - } - if (matches(elt, 'form')) { - var inputs = elt.elements; - forEach(inputs, function(input) { - processInputValue(processed, values, errors, input, validate); - }); - } - } - - function validateElement(element, errors) { - if (element.willValidate) { - triggerEvent(element, "htmx:validation:validate") - if (!element.checkValidity()) { - errors.push({elt: element, message:element.validationMessage, validity:element.validity}); - triggerEvent(element, "htmx:validation:failed", {message:element.validationMessage, validity:element.validity}) - } - } - } - - /** - * @param {HTMLElement} elt - * @param {string} verb - */ - function getInputValues(elt, verb) { - var processed = []; - var values = {}; - var formValues = {}; - var errors = []; - var internalData = getInternalData(elt); - - // only validate when form is directly submitted and novalidate or formnovalidate are not set - // 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; - } - - // for a non-GET include the closest form - if (verb !== 'get') { - processInputValue(processed, formValues, errors, closest(elt, 'form'), validate); - } - - // include the element itself - 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; - } - } - - // include any explicit includes - var includes = findAttributeTargets(elt, "hx-include"); - forEach(includes, function(node) { - processInputValue(processed, values, errors, node, validate); - // if a non-form is included, include any input values within it - if (!matches(node, 'form')) { - forEach(node.querySelectorAll(INPUT_SELECTOR), function (descendant) { - processInputValue(processed, values, errors, descendant, validate); - }) - } - }); - - // form values take precedence, overriding the regular values - values = mergeObjects(values, formValues); - - return {errors:errors, values:values}; - } - - function appendParam(returnStr, name, realValue) { - if (returnStr !== "") { - returnStr += "&"; - } - if (String(realValue) === "[object Object]") { - realValue = JSON.stringify(realValue); - } - var s = encodeURIComponent(realValue); - returnStr += encodeURIComponent(name) + "=" + s; - return returnStr; - } - - function urlEncode(values) { - var returnStr = ""; - for (var name in values) { - if (values.hasOwnProperty(name)) { - var value = values[name]; - if (Array.isArray(value)) { - forEach(value, function(v) { - returnStr = appendParam(returnStr, name, v); - }); - } else { - returnStr = appendParam(returnStr, name, value); - } - } - } - return returnStr; - } - - function makeFormData(values) { - var formData = new FormData(); - for (var name in values) { - if (values.hasOwnProperty(name)) { - var value = values[name]; - if (Array.isArray(value)) { - forEach(value, function(v) { - formData.append(name, v); - }); - } else { - formData.append(name, value); - } - } - } - return formData; - } - - //==================================================================== - // Ajax - //==================================================================== - - /** - * @param {HTMLElement} elt - * @param {HTMLElement} target - * @param {string} prompt - * @returns {Object} // TODO: Define/Improve HtmxHeaderSpecification - */ - function getHeaders(elt, target, prompt) { - var headers = { - "HX-Request" : "true", - "HX-Trigger" : getRawAttribute(elt, "id"), - "HX-Trigger-Name" : getRawAttribute(elt, "name"), - "HX-Target" : getAttributeValue(target, "id"), - "HX-Current-URL" : getDocument().location.href, - } - getValuesForElement(elt, "hx-headers", false, headers) - if (prompt !== undefined) { - headers["HX-Prompt"] = prompt; - } - if (getInternalData(elt).boosted) { - headers["HX-Boosted"] = "true"; - } - return headers; - } - - /** - * filterValues takes an object containing form input values - * and returns a new object that only contains keys that are - * specified by the closest "hx-params" attribute - * @param {Object} inputValues - * @param {HTMLElement} elt - * @returns {Object} - */ - function filterValues(inputValues, elt) { - var paramsValue = getClosestAttributeValue(elt, "hx-params"); - if (paramsValue) { - if (paramsValue === "none") { - return {}; - } else if (paramsValue === "*") { - return inputValues; - } else if(paramsValue.indexOf("not ") === 0) { - forEach(paramsValue.substr(4).split(","), function (name) { - name = name.trim(); - delete inputValues[name]; - }); - return inputValues; - } else { - var newValues = {} - forEach(paramsValue.split(","), function (name) { - name = name.trim(); - newValues[name] = inputValues[name]; - }); - return newValues; - } - } else { - return inputValues; - } - } - - function isAnchorLink(elt) { - return getRawAttribute(elt, 'href') && getRawAttribute(elt, 'href').indexOf("#") >=0 - } - - /** - * - * @param {HTMLElement} elt - * @param {string} swapInfoOverride - * @returns {import("./htmx").HtmxSwapSpecification} - */ - function getSwapSpecification(elt, swapInfoOverride) { - var swapInfo = swapInfoOverride ? swapInfoOverride : getClosestAttributeValue(elt, "hx-swap"); - var swapSpec = { - "swapStyle" : getInternalData(elt).boosted ? 'innerHTML' : htmx.config.defaultSwapStyle, - "swapDelay" : htmx.config.defaultSwapDelay, - "settleDelay" : htmx.config.defaultSettleDelay - } - if (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("transition:") === 0) { - swapSpec["transition"] = modifier.substr(11) === "true"; - } - if (modifier.indexOf("scroll:") === 0) { - var scrollSpec = modifier.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); - 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); - swapSpec["focusScroll"] = focusScrollVal == "true"; - } - } - } - } - 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) { - if (encodedParameters == null) { - encodedParameters = extension.encodeParameters(xhr, filteredParameters, elt); - } - }); - if (encodedParameters != null) { - return encodedParameters; - } else { - if (usesFormData(elt)) { - return makeFormData(filteredParameters); - } else { - return urlEncode(filteredParameters); - } - } - } - - /** - * - * @param {Element} target - * @returns {import("./htmx").HtmxSettleInfo} - */ - function makeSettleInfo(target) { - return {tasks: [], elts: [target]}; - } - - function updateScrollState(content, swapSpec) { - var first = content[0]; - var last = content[content.length - 1]; - if (swapSpec.scroll) { - var target = null; - if (swapSpec.scrollTarget) { - target = querySelectorExt(first, swapSpec.scrollTarget); - } - if (swapSpec.scroll === "top" && (first || target)) { - target = target || first; - target.scrollTop = 0; - } - if (swapSpec.scroll === "bottom" && (last || target)) { - target = target || last; - target.scrollTop = target.scrollHeight; - } - } - if (swapSpec.show) { - var target = null; - if (swapSpec.showTarget) { - var targetStr = swapSpec.showTarget; - if (swapSpec.showTarget === "window") { - targetStr = "body"; - } - target = querySelectorExt(first, targetStr); - } - if (swapSpec.show === "top" && (first || target)) { - target = target || first; - target.scrollIntoView({block:'start', behavior: htmx.config.scrollBehavior}); - } - if (swapSpec.show === "bottom" && (last || target)) { - target = target || last; - target.scrollIntoView({block:'end', behavior: htmx.config.scrollBehavior}); - } - } - } - - /** - * @param {HTMLElement} elt - * @param {string} attr - * @param {boolean=} evalAsDefault - * @param {Object=} values - * @returns {Object} - */ - function getValuesForElement(elt, attr, evalAsDefault, values) { - if (values == null) { - values = {}; - } - if (elt == null) { - return values; - } - var attributeValue = getAttributeValue(elt, attr); - 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; - } else if (str.indexOf("js:") === 0) { - str = str.substr(3); - evaluateValue = true; - } - if (str.indexOf('{') !== 0) { - str = "{" + str + "}"; - } - var varsValues; - if (evaluateValue) { - varsValues = maybeEval(elt,function () {return Function("return (" + str + ")")();}, {}); - } else { - varsValues = parseJSON(str); - } - for (var key in varsValues) { - if (varsValues.hasOwnProperty(key)) { - if (values[key] == null) { - values[key] = varsValues[key]; - } - } - } - } - return getValuesForElement(parentElt(elt), attr, evalAsDefault, values); - } - - function maybeEval(elt, toEval, defaultVal) { - if (htmx.config.allowEval) { - return toEval(); - } else { - triggerErrorEvent(elt, 'htmx:evalDisallowedError'); - return defaultVal; - } - } - - /** - * @param {HTMLElement} elt - * @param {*} expressionVars - * @returns - */ - function getHXVarsForElement(elt, expressionVars) { - return getValuesForElement(elt, "hx-vars", true, expressionVars); - } - - /** - * @param {HTMLElement} elt - * @param {*} expressionVars - * @returns - */ - function getHXValsForElement(elt, expressionVars) { - return getValuesForElement(elt, "hx-vals", false, expressionVars); - } - - /** - * @param {HTMLElement} elt - * @returns {Object} - */ - function getExpressionVars(elt) { - return mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt)); - } - - function safelySetHeaderValue(xhr, header, headerValue) { - if (headerValue !== null) { - try { - xhr.setRequestHeader(header, headerValue); - } catch (e) { - // On an exception, try to set the header URI encoded instead - xhr.setRequestHeader(header, encodeURIComponent(headerValue)); - xhr.setRequestHeader(header + "-URI-AutoEncoded", "true"); - } - } - } - - function getPathFromResponse(xhr) { - // NB: IE11 does not support this stuff - if (xhr.responseURL && typeof(URL) !== "undefined") { - try { - var url = new URL(xhr.responseURL); - return url.pathname + url.search; - } catch (e) { - triggerErrorEvent(getDocument().body, "htmx:badResponseUrl", {url: xhr.responseURL}); - } - } - } - - function hasHeader(xhr, regexp) { - return xhr.getAllResponseHeaders().match(regexp); - } - - function ajaxHelper(verb, path, context) { - verb = verb.toLowerCase(); - if (context) { - if (context instanceof Element || isType(context, 'String')) { - return issueAjaxRequest(verb, path, null, null, { - targetOverride: resolveTarget(context), - returnPromise: true - }); - } else { - return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event, - { - handler : context.handler, - headers : context.headers, - values : context.values, - targetOverride: resolveTarget(context.target), - swapOverride: context.swap, - returnPromise: true - }); - } - } else { - return issueAjaxRequest(verb, path, null, null, { - returnPromise: true - }); - } - } - - function hierarchyForElt(elt) { - var arr = []; - while (elt) { - arr.push(elt); - elt = elt.parentElement; - } - return arr; - } - - function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { - var resolve = null; - var reject = null; - etc = etc != null ? etc : {}; - if(etc.returnPromise && typeof Promise !== "undefined"){ - var promise = new Promise(function (_resolve, _reject) { - resolve = _resolve; - reject = _reject; - }); - } - if(elt == null) { - elt = getDocument().body; - } - var responseHandler = etc.handler || handleAjaxResponse; - - if (!bodyContains(elt)) { - return; // do not issue requests for elements removed from the DOM - } - var target = etc.targetOverride || getTarget(elt); - if (target == null || target == DUMMY_ELT) { - triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")}); - return; - } - - // allow event-based confirmation w/ a callback - if (!confirmed) { - var issueRequest = function() { - return issueAjaxRequest(verb, path, elt, event, etc, true); - } - var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest}; - if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { - return; - } - } - - var syncElt = elt; - var eltData = getInternalData(elt); - var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); - var queueStrategy = null; - var abortable = false; - if (syncStrategy) { - var syncStrings = syncStrategy.split(":"); - var selector = syncStrings[0].trim(); - if (selector === "this") { - syncElt = findThisElement(elt, 'hx-sync'); - } else { - syncElt = querySelectorExt(elt, selector); - } - // default to the drop strategy - syncStrategy = (syncStrings[1] || 'drop').trim(); - eltData = getInternalData(syncElt); - if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { - return; - } else if (syncStrategy === "abort") { - if (eltData.xhr) { - return; - } else { - abortable = true; - } - } else if (syncStrategy === "replace") { - triggerEvent(syncElt, 'htmx:abort'); // abort the current request and continue - } else if (syncStrategy.indexOf("queue") === 0) { - var queueStrArray = syncStrategy.split(" "); - queueStrategy = (queueStrArray[1] || "last").trim(); - } - } - - if (eltData.xhr) { - if (eltData.abortable) { - triggerEvent(syncElt, 'htmx:abort'); // abort the current request and continue - } else { - if(queueStrategy == null){ - if (event) { - var eventData = getInternalData(event); - if (eventData && eventData.triggerSpec && eventData.triggerSpec.queue) { - queueStrategy = eventData.triggerSpec.queue; - } - } - if (queueStrategy == null) { - queueStrategy = "last"; - } - } - if (eltData.queuedRequests == null) { - eltData.queuedRequests = []; - } - if (queueStrategy === "first" && eltData.queuedRequests.length === 0) { - eltData.queuedRequests.push(function () { - issueAjaxRequest(verb, path, elt, event, etc) - }); - } else if (queueStrategy === "all") { - eltData.queuedRequests.push(function () { - issueAjaxRequest(verb, path, elt, event, etc) - }); - } else if (queueStrategy === "last") { - eltData.queuedRequests = []; // dump existing queue - eltData.queuedRequests.push(function () { - issueAjaxRequest(verb, path, elt, event, etc) - }); - } - return; - } - } - - var xhr = new XMLHttpRequest(); - eltData.xhr = xhr; - eltData.abortable = abortable; - var endRequestLock = function(){ - eltData.xhr = null; - eltData.abortable = false; - if (eltData.queuedRequests != null && - eltData.queuedRequests.length > 0) { - var queuedRequest = eltData.queuedRequests.shift(); - queuedRequest(); - } - } - var promptQuestion = getClosestAttributeValue(elt, "hx-prompt"); - if (promptQuestion) { - var promptResponse = prompt(promptQuestion); - // prompt returns null if cancelled and empty string if accepted with no entry - if (promptResponse === null || - !triggerEvent(elt, 'htmx:prompt', {prompt: promptResponse, target:target})) { - maybeCall(resolve); - endRequestLock(); - return promise; - } - } - - var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); - if (confirmQuestion) { - if(!confirm(confirmQuestion)) { - maybeCall(resolve); - endRequestLock() - return promise; - } - } - - - var headers = getHeaders(elt, target, promptResponse); - if (etc.headers) { - headers = mergeObjects(headers, etc.headers); - } - var results = getInputValues(elt, verb); - var errors = results.errors; - var rawParameters = results.values; - if (etc.values) { - rawParameters = mergeObjects(rawParameters, etc.values); - } - var expressionVars = getExpressionVars(elt); - var allParameters = mergeObjects(rawParameters, expressionVars); - var filteredParameters = filterValues(allParameters, elt); - - if (verb !== 'get' && !usesFormData(elt)) { - 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 - if (path == null || path === "") { - 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, - target:target, - verb:verb, - errors:errors, - withCredentials: etc.credentials || requestAttrValues.credentials || htmx.config.withCredentials, - timeout: etc.timeout || requestAttrValues.timeout || htmx.config.timeout, - path:path, - triggeringEvent:event - }; - - if(!triggerEvent(elt, 'htmx:configRequest', requestConfig)){ - maybeCall(resolve); - endRequestLock(); - return promise; - } - - // copy out in case the object was overwritten - path = requestConfig.path; - verb = requestConfig.verb; - headers = requestConfig.headers; - filteredParameters = requestConfig.parameters; - errors = requestConfig.errors; - useUrlParams = requestConfig.useUrlParams; - - if(errors && errors.length > 0){ - triggerEvent(elt, 'htmx:validation:halted', requestConfig) - maybeCall(resolve); - endRequestLock(); - return promise; - } - - var splitPath = path.split("#"); - var pathNoAnchor = splitPath[0]; - var anchor = splitPath[1]; - - var finalPath = path - if (useUrlParams) { - finalPath = pathNoAnchor; - var values = Object.keys(filteredParameters).length !== 0; - if (values) { - if (finalPath.indexOf("?") < 0) { - finalPath += "?"; - } else { - finalPath += "&"; - } - finalPath += urlEncode(filteredParameters); - if (anchor) { - finalPath += "#" + anchor; - } - } - } - - xhr.open(verb.toUpperCase(), finalPath, true); - xhr.overrideMimeType("text/html"); - xhr.withCredentials = requestConfig.withCredentials; - xhr.timeout = requestConfig.timeout; - - // request headers - if (requestAttrValues.noHeaders) { - // ignore all headers - } else { - for (var header in headers) { - if (headers.hasOwnProperty(header)) { - var headerValue = headers[header]; - safelySetHeaderValue(xhr, header, headerValue); - } - } - } - - var responseInfo = { - xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, - 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); - triggerEvent(elt, 'htmx:afterRequest', responseInfo); - triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); - // 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; - while (hierarchy.length > 0 && secondaryTriggerElt == null) { - var parentEltInHierarchy = hierarchy.shift(); - if (bodyContains(parentEltInHierarchy)) { - secondaryTriggerElt = parentEltInHierarchy; - } - } - if (secondaryTriggerElt) { - triggerEvent(secondaryTriggerElt, 'htmx:afterRequest', responseInfo); - triggerEvent(secondaryTriggerElt, 'htmx:afterOnLoad', responseInfo); - } - } - maybeCall(resolve); - endRequestLock(); - } catch (e) { - triggerErrorEvent(elt, 'htmx:onLoadError', mergeObjects({error:e}, responseInfo)); - throw e; - } - } - xhr.onerror = function () { - removeRequestIndicatorClasses(indicators); - triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); - triggerErrorEvent(elt, 'htmx:sendError', responseInfo); - maybeCall(reject); - endRequestLock(); - } - xhr.onabort = function() { - removeRequestIndicatorClasses(indicators); - triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); - triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); - maybeCall(reject); - endRequestLock(); - } - xhr.ontimeout = function() { - removeRequestIndicatorClasses(indicators); - triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); - triggerErrorEvent(elt, 'htmx:timeout', responseInfo); - maybeCall(reject); - endRequestLock(); - } - if(!triggerEvent(elt, 'htmx:beforeRequest', responseInfo)){ - maybeCall(resolve); - endRequestLock() - return promise - } - var indicators = addRequestIndicatorClasses(elt); - - forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) { - forEach([xhr, xhr.upload], function (target) { - target.addEventListener(eventName, function(event){ - triggerEvent(elt, "htmx:xhr:" + eventName, { - lengthComputable:event.lengthComputable, - loaded:event.loaded, - total:event.total - }); - }) - }); - }); - triggerEvent(elt, 'htmx:beforeSend', responseInfo); - 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; - - if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; - - if (hasHeader(xhr, /HX-Trigger:/i)) { - handleTrigger(xhr, "HX-Trigger", elt); - } - - 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; - } - - if (hasHeader(xhr, /HX-Redirect:/i)) { - location.href = xhr.getResponseHeader("HX-Redirect"); - return; - } - - if (hasHeader(xhr,/HX-Refresh:/i)) { - if ("true" === xhr.getResponseHeader("HX-Refresh")) { - location.reload(); - return; - } - } - - if (hasHeader(xhr,/HX-Retarget:/i)) { - responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget")); - } - - var historyUpdate = determineHistoryUpdates(elt, responseInfo); - - // by default htmx only swaps on 200 return codes and does not swap - // on 204 'No Content' - // this can be ovverriden by responding to the htmx:beforeSwap event and - // overriding the detail.shouldSwap property - 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); - 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 - - 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 - - if (beforeSwapDetails.shouldSwap) { - if (xhr.status === 286) { - cancelPolling(elt); - } - - withExtensions(elt, function (extension) { - serverResponse = extension.transformResponse(serverResponse, xhr, elt); - }); - - // 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); - - 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 { - selectionInfo = { - elt: activeElt, - // @ts-ignore - start: activeElt ? activeElt.selectionStart : null, - // @ts-ignore - end: activeElt ? activeElt.selectionEnd : null - }; - } catch (e) { - // safari issue - see https://github.com/microsoft/playwright/issues/5894 - } - - var selectOverride; - if (hasHeader(xhr, /HX-Reselect:/i)) { - selectOverride = xhr.getResponseHeader("HX-Reselect"); - } - - var settleInfo = makeSettleInfo(target); - selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); - - if (selectionInfo.elt && - !bodyContains(selectionInfo.elt) && - selectionInfo.elt.id) { - var newActiveElt = document.getElementById(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 - 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); - } - } - - target.classList.remove(htmx.config.swappingClass); - forEach(settleInfo.elts, function (elt) { - if (elt.classList) { - elt.classList.add(htmx.config.settlingClass); - } - triggerEvent(elt, 'htmx:afterSwap', responseInfo); - }); - - if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { - var finalElt = elt; - if (!bodyContains(elt)) { - finalElt = getDocument().body; - } - handleTrigger(xhr, "HX-Trigger-After-Swap", finalElt); - } - - var doSettle = function () { - forEach(settleInfo.tasks, function (task) { - task.call(); - }); - forEach(settleInfo.elts, function (elt) { - if (elt.classList) { - elt.classList.remove(htmx.config.settlingClass); - } - triggerEvent(elt, 'htmx:afterSettle', responseInfo); - }); - - // if we need to save history, do so - if (historyUpdate.type) { - 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}); - } - } - if (responseInfo.pathInfo.anchor) { - var anchorTarget = find("#" + responseInfo.pathInfo.anchor); - if(anchorTarget) { - anchorTarget.scrollIntoView({block:'start', behavior: "auto"}); - } - } - - if(settleInfo.title) { - var titleElt = find("title"); - if(titleElt) { - titleElt.innerHTML = settleInfo.title; - } else { - window.document.title = settleInfo.title; - } - } - - updateScrollState(settleInfo.elts, swapSpec); - - if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) { - var finalElt = elt; - if (!bodyContains(elt)) { - finalElt = getDocument().body; - } - handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); - } - maybeCall(settleResolve); - } - - if (swapSpec.settleDelay > 0) { - setTimeout(doSettle, swapSpec.settleDelay) - } else { - doSettle(); - } - } 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 { - doSwap(); - } - } - if (isError) { - triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath}, responseInfo)); - } - } - - //==================================================================== - // Extensions API - //==================================================================== - - /** @type {Object} */ - var extensions = {}; - - /** - * extensionBase defines the default functions for all extensions. - * @returns {import("./htmx").HtmxExtension} - */ - function extensionBase() { - return { - init: function(api) {return null;}, - onEvent : function(name, evt) {return true;}, - transformResponse : function(text, xhr, elt) {return text;}, - isInlineSwap : function(swapStyle) {return false;}, - handleSwap : function(swapStyle, target, fragment, settleInfo) {return false;}, - encodeParameters : function(xhr, parameters, elt) {return null;} - } - } - - /** - * defineExtension initializes the extension and adds it to the htmx registry - * - * @param {string} name - * @param {import("./htmx").HtmxExtension} extension - */ - function defineExtension(name, extension) { - if(extension.init) { - extension.init(internalAPI) - } - extensions[name] = mergeObjects(extensionBase(), extension); - } - - /** - * removeExtension removes an extension from the htmx registry - * - * @param {string} name - */ - function removeExtension(name) { - delete extensions[name]; - } - - /** - * getExtensions searches up the DOM tree to return all extensions that can be applied to a given element - * - * @param {HTMLElement} elt - * @param {import("./htmx").HtmxExtension[]=} extensionsToReturn - * @param {import("./htmx").HtmxExtension[]=} extensionsToIgnore - */ - function getExtensions(elt, extensionsToReturn, extensionsToIgnore) { - - if (elt == undefined) { - return extensionsToReturn; - } - if (extensionsToReturn == undefined) { - extensionsToReturn = []; - } - if (extensionsToIgnore == undefined) { - extensionsToIgnore = []; - } - var extensionsForElement = getAttributeValue(elt, "hx-ext"); - if (extensionsForElement) { - forEach(extensionsForElement.split(","), function(extensionName){ - extensionName = extensionName.replace(/ /g, ''); - if (extensionName.slice(0, 7) == "ignore:") { - extensionsToIgnore.push(extensionName.slice(7)); - return; - } - if (extensionsToIgnore.indexOf(extensionName) < 0) { - var extension = extensions[extensionName]; - if (extension && extensionsToReturn.indexOf(extension) < 0) { - extensionsToReturn.push(extension); - } - } - }); - } - return getExtensions(parentElt(elt), extensionsToReturn, extensionsToIgnore); - } - - //==================================================================== - // Initialization - //==================================================================== - - function ready(fn) { - if (getDocument().readyState !== 'loading') { - fn(); - } else { - getDocument().addEventListener('DOMContentLoaded', fn); - } - } - - function insertIndicatorStyles() { - if (htmx.config.includeIndicatorStyles !== false) { - getDocument().head.insertAdjacentHTML("beforeend", - ""); - } - } - - function getMetaConfig() { - var element = getDocument().querySelector('meta[name="htmx-config"]'); - if (element) { - // @ts-ignore - return parseJSON(element.content); - } else { - return null; - } - } - - function mergeMetaConfig() { - var metaConfig = getMetaConfig(); - if (metaConfig) { - htmx.config = mergeObjects(htmx.config , metaConfig) - } - } - - // initialize the document - ready(function () { - mergeMetaConfig(); - insertIndicatorStyles(); - var body = getDocument().body; - processNode(body); - var restoredElts = getDocument().querySelectorAll( - "[hx-trigger='restored'],[data-hx-trigger='restored']" - ); - body.addEventListener("htmx:abort", function (evt) { - var target = evt.target; - var internalData = getInternalData(target); - if (internalData && internalData.xhr) { - internalData.xhr.abort(); - } - }); - var originalPopstate = window.onpopstate; - window.onpopstate = function (event) { - if (event.state && event.state.htmx) { - restoreHistory(); - forEach(restoredElts, function(elt){ - triggerEvent(elt, 'htmx:restored', { - 'document': getDocument(), - '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); - }) - - return htmx; - } -)() -})); +(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 G={onLoad:t,process:Nt,on:ue,off:fe,trigger:oe,ajax:mr,find:b,findAll:f,closest:d,values:function(e,t){var r=Qt(e,t||"post");return r.values},remove:B,addClass:V,removeClass:n,toggleClass:j,takeClass:W,defineExtension:Er,removeExtension:Cr,logAll:F,logNone:U,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false,methodsThatUseUrlParams:["get"]},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=G.config.wsBinaryType;return t},version:"1.9.3"};var C={addTriggerHandler:bt,bodyContains:re,canAccessLocalStorage:D,findThisElement:de,filterValues:ir,hasAttribute:q,getAttributeValue:Z,getClosestAttributeValue:Y,getClosestMatch:c,getExpressionVars:vr,getHeaders:nr,getInputValues:Qt,getInternalData:ee,getSwapSpecification:or,getTriggerSpecs:Ge,getTarget:ve,makeFragment:l,mergeObjects:ne,makeSettleInfo:S,oobSwap:xe,querySelectorExt:ie,selectAndSwap:Xe,settleImmediately:Wt,shouldCancel:Qe,triggerEvent:oe,triggerErrorEvent:ae,withExtensions:w};var R=["get","post","put","delete","patch"];var O=R.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function v(e){if(e==undefined){return undefined}if(e.slice(-2)=="ms"){return parseFloat(e.slice(0,-2))||undefined}if(e.slice(-1)=="s"){return parseFloat(e.slice(0,-1))*1e3||undefined}if(e.slice(-1)=="m"){return parseFloat(e.slice(0,-1))*1e3*60||undefined}return parseFloat(e)||undefined}function J(e,t){return e.getAttribute&&e.getAttribute(t)}function q(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function Z(e,t){return J(e,t)||J(e,"data-"+t)}function u(e){return e.parentElement}function K(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function T(e,t,r){var n=Z(t,r);var i=Z(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function Y(t,r){var n=null;c(t,function(e){return n=T(t,e,r)});if(n!=="unset"){return n}}function h(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function H(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function i(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=K().createDocumentFragment()}return i}function L(e){return e.match(/",0);return r.querySelector("template").content}else{var n=H(e);switch(n){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return i(""+e+"
",1);case"col":return i(""+e+"
",2);case"tr":return i(""+e+"
",2);case"td":case"th":return i(""+e+"
",3);case"script":return i("
"+e+"
",1);default:return i(e,0)}}}function Q(e){if(e){e()}}function A(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function N(e){return A(e,"Function")}function I(e){return A(e,"Object")}function ee(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function k(e){var t=[];if(e){for(var r=0;r=0}function re(e){if(e.getRootNode&&e.getRootNode()instanceof ShadowRoot){return K().body.contains(e.getRootNode().host)}else{return K().body.contains(e)}}function M(e){return e.trim().split(/\s+/)}function ne(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function y(e){try{return JSON.parse(e)}catch(e){x(e);return null}}function D(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function X(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!t.match("^/$")){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function e(e){return cr(K().body,function(){return eval(e)})}function t(t){var e=G.on("htmx:load",function(e){t(e.detail.elt)});return e}function F(){G.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function U(){G.logger=null}function b(e,t){if(t){return e.querySelector(t)}else{return b(K(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(K(),e)}}function B(e,t){e=s(e);if(t){setTimeout(function(){B(e);e=null},t)}else{e.parentElement.removeChild(e)}}function V(e,t,r){e=s(e);if(r){setTimeout(function(){V(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=s(e);if(r){setTimeout(function(){n(e,t);e=null},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function j(e,t){e=s(e);e.classList.toggle(t)}function W(e,t){e=s(e);te(e.parentElement.children,function(e){n(e,t)});V(e,t)}function d(e,t){e=s(e);if(e.closest){return e.closest(t)}else{do{if(e==null||h(e,t)){return e}}while(e=e&&u(e));return null}}function r(e){var t=e.trim();if(t.startsWith("<")&&t.endsWith("/>")){return t.substring(1,t.length-2)}else{return t}}function _(e,t){if(t.indexOf("closest ")===0){return[d(e,r(t.substr(8)))]}else if(t.indexOf("find ")===0){return[b(e,r(t.substr(5)))]}else if(t.indexOf("next ")===0){return[z(e,r(t.substr(5)))]}else if(t.indexOf("previous ")===0){return[$(e,r(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return K().querySelectorAll(r(t))}}var z=function(e,t){var r=K().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ie(e,t){if(t){return _(e,t)[0]}else{return _(K().body,e)[0]}}function s(e){if(A(e,"String")){return b(e)}else{return e}}function le(e,t,r){if(N(t)){return{target:K().body,event:e,listener:t}}else{return{target:s(e),event:t,listener:r}}}function ue(t,r,n){Or(function(){var e=le(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=N(r);return e?r:n}function fe(t,r,n){Or(function(){var e=le(t,r,n);e.target.removeEventListener(e.event,e.listener)});return N(r)?r:n}var ce=K().createElement("output");function he(e,t){var r=Y(e,t);if(r){if(r==="this"){return[de(e,t)]}else{var n=_(e,r);if(n.length===0){x('The selector "'+r+'" on '+t+" returned no matches!");return[ce]}else{return n}}}}function de(e,t){return c(e,function(e){return Z(e,t)!=null})}function ve(e){var t=Y(e,"hx-target");if(t){if(t==="this"){return de(e,"hx-target")}else{return ie(e,t)}}else{var r=ee(e);if(r.boosted){return K().body}else{return e}}}function ge(e){var t=G.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=K().querySelectorAll(t);if(r){te(r,function(e){var t;var r=i.cloneNode(true);t=K().createDocumentFragment();t.appendChild(r);if(!me(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!oe(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Me(o,e,e,t,a)}te(a.elts,function(e){oe(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);ae(K().body,"htmx:oobErrorNoTarget",{content:i})}return e}function ye(e,t,r){var n=Y(e,"hx-select-oob");if(n){var i=n.split(",");for(let e=0;e0){var t=e.id.replace("'","\\'");var r=e.tagName.replace(":","\\:");var n=a.querySelector(r+"[id='"+t+"']");if(n&&n!==a){var i=e.cloneNode();pe(e,n);o.tasks.push(function(){pe(e,i)})}}})}function Se(e){return function(){n(e,G.config.addedClass);Nt(e);St(e);Ee(e);oe(e,"htmx:load")}}function Ee(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function a(e,t,r,n){we(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;V(i,G.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Se(i))}}}function Ce(e,t){var r=0;while(re!=t);while(n&&n!==t){if(n.nodeType===Node.ELEMENT_NODE){r.elts.push(n)}n=n.nextElementSibling}o(t);u(t).removeChild(t)}}function He(e,t,r){return a(e,e.firstChild,t,r)}function Le(e,t,r){return a(u(e),e,t,r)}function Ae(e,t,r){return a(e,null,t,r)}function Ne(e,t,r){return a(u(e),e.nextSibling,t,r)}function Ie(e,t,r){o(e);return u(e).removeChild(e)}function ke(e,t,r){var n=e.firstChild;a(e,n,t,r);if(n){while(n.nextSibling){o(n.nextSibling);e.removeChild(n.nextSibling)}o(n);e.removeChild(n)}}function Pe(e,t,r){var n=r||Y(e,"hx-select");if(n){var i=K().createDocumentFragment();te(t.querySelectorAll(n),function(e){i.appendChild(e)});t=i}return t}function Me(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":Te(r,n,i);return;case"afterbegin":He(r,n,i);return;case"beforebegin":Le(r,n,i);return;case"beforeend":Ae(r,n,i);return;case"afterend":Ne(r,n,i);return;case"delete":Ie(r,n,i);return;default:var a=Rr(t);for(var o=0;o-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function Xe(e,t,r,n,i,a){i.title=De(n);var o=l(n);if(o){ye(r,o,i);o=Pe(r,o,a);be(o);return Me(e,r,t,o,i)}}function Fe(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=y(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!I(o)){o={value:o}}oe(r,a,o)}}}else{oe(r,n,[])}}var Ue=/\s/;var g=/[\s,]/;var Be=/[_$a-zA-Z]/;var Ve=/[_$a-zA-Z0-9]/;var je=['"',"'","/"];var p=/[^\s]/;function We(e){var t=[];var r=0;while(r0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=cr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){ae(K().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(_e(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function m(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var $e="input, textarea, select";function Ge(e){var t=Z(e,"hx-trigger");var r=[];if(t){var n=We(t);do{m(n,p);var i=n.length;var a=m(n,/[,\[\s]/);if(a!==""){if(a==="every"){var o={trigger:"every"};m(n,p);o.pollInterval=v(m(n,/[,\[\s]/));m(n,p);var s=ze(e,n,"event");if(s){o.eventFilter=s}r.push(o)}else if(a.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:a.substr(4)})}else{var l={trigger:a};var s=ze(e,n,"event");if(s){l.eventFilter=s}while(n.length>0&&n[0]!==","){m(n,p);var u=n.shift();if(u==="changed"){l.changed=true}else if(u==="once"){l.once=true}else if(u==="consume"){l.consume=true}else if(u==="delay"&&n[0]===":"){n.shift();l.delay=v(m(n,g))}else if(u==="from"&&n[0]===":"){n.shift();var f=m(n,g);if(f==="closest"||f==="find"||f==="next"||f==="previous"){n.shift();f+=" "+m(n,g)}l.from=f}else if(u==="target"&&n[0]===":"){n.shift();l.target=m(n,g)}else if(u==="throttle"&&n[0]===":"){n.shift();l.throttle=v(m(n,g))}else if(u==="queue"&&n[0]===":"){n.shift();l.queue=m(n,g)}else if((u==="root"||u==="threshold")&&n[0]===":"){n.shift();l[u]=m(n,g)}else{ae(e,"htmx:syntax:error",{token:n.shift()})}}r.push(l)}}if(n.length===i){ae(e,"htmx:syntax:error",{token:n.shift()})}m(n,p)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"]')){return[{trigger:"click"}]}else if(h(e,$e)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Je(e){ee(e).cancelled=true}function Ze(e,t,r){var n=ee(e);n.timeout=setTimeout(function(){if(re(e)&&n.cancelled!==true){if(!tt(r,e,kt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}Ze(e,t,r)}},r.pollInterval)}function Ke(e){return location.hostname===e.hostname&&J(e,"href")&&J(e,"href").indexOf("#")!==0}function Ye(t,r,e){if(t.tagName==="A"&&Ke(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=t.href}else{var a=J(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=J(t,"action")}e.forEach(function(e){rt(t,function(e,t){se(n,i,e,t)},r,e,true)})}}function Qe(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&d(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function et(e,t){return ee(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function tt(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){ae(K().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function rt(i,a,e,o,s){var l=ee(i);var t;if(o.from){t=_(i,o.from)}else{t=[i]}if(o.changed){l.lastValue=i.value}te(t,function(r){var n=function(e){if(!re(i)){r.removeEventListener(o.trigger,n);return}if(et(i,e)){return}if(s||Qe(e,i)){e.preventDefault()}if(tt(o,i,e)){return}var t=ee(e);t.triggerSpec=o;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(i)<0){t.handledFor.push(i);if(o.consume){e.stopPropagation()}if(o.target&&e.target){if(!h(e.target,o.target)){return}}if(o.once){if(l.triggeredOnce){return}else{l.triggeredOnce=true}}if(o.changed){if(l.lastValue===i.value){return}else{l.lastValue=i.value}}if(l.delayed){clearTimeout(l.delayed)}if(l.throttle){return}if(o.throttle){if(!l.throttle){a(i,e);l.throttle=setTimeout(function(){l.throttle=null},o.throttle)}}else if(o.delay){l.delayed=setTimeout(function(){a(i,e)},o.delay)}else{oe(i,"htmx:trigger");a(i,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:o.trigger,listener:n,on:r});r.addEventListener(o.trigger,n)})}var nt=false;var it=null;function at(){if(!it){it=function(){nt=true};window.addEventListener("scroll",it);setInterval(function(){if(nt){nt=false;te(K().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){ot(e)})}},200)}}function ot(t){if(!q(t,"data-hx-revealed")&&P(t)){t.setAttribute("data-hx-revealed","true");var e=ee(t);if(e.initHash){oe(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){oe(t,"revealed")},{once:true})}}}function st(e,t,r){var n=M(r);for(var i=0;i=0){var t=ct(n);setTimeout(function(){lt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ee(s).webSocket=t;t.addEventListener("message",function(e){if(ut(s)){return}var t=e.data;w(s,function(e){t=e.transformResponse(t,null,s)});var r=S(s);var n=l(t);var i=k(n.children);for(var a=0;a0){oe(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Qe(e,u)){e.preventDefault()}})}else{ae(u,"htmx:noWebSocketSourceError")}}function ct(e){var t=G.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()}x('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function ht(e,t,r){var n=M(r);for(var i=0;i0){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+=qt(o)}for(var l in r){Tt(e,l,r[l])}}}function Lt(t){Oe(t);for(const e of t.attributes){const{name:r,value:n}=e;if(r.startsWith("hx-on:")||r.startsWith("data-hx-on:")){let e=r.slice(r.indexOf(":")+1);if(e.startsWith(":"))e="htmx"+e;Tt(t,e,n)}}}function At(t){if(t.closest&&t.closest(G.config.disableSelector)){return}var r=ee(t);if(r.initHash!==Re(t)){r.initHash=Re(t);qe(t);Ht(t);oe(t,"htmx:beforeProcessNode");if(t.value){r.lastValue=t.value}var e=Ge(t);var n=yt(t,r,e);if(!n){if(Y(t,"hx-boost")==="true"){Ye(t,r,e)}else if(q(t,"hx-trigger")){e.forEach(function(e){bt(t,e,r,function(){})})}}if(t.tagName==="FORM"){Ot(t)}var i=Z(t,"hx-sse");if(i){ht(t,r,i)}var a=Z(t,"hx-ws");if(a){st(t,r,a)}oe(t,"htmx:afterProcessNode")}}function Nt(e){e=s(e);At(e);te(Rt(e),function(e){At(e)});te(Ct(e),Lt)}function It(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function kt(e,t){var r;if(window.CustomEvent&&typeof window.CustomEvent==="function"){r=new CustomEvent(e,{bubbles:true,cancelable:true,detail:t})}else{r=K().createEvent("CustomEvent");r.initCustomEvent(e,true,true,t)}return r}function ae(e,t,r){oe(e,t,ne({error:t},r))}function Pt(e){return e==="htmx:afterProcessNode"}function w(e,t){te(Rr(e),function(e){try{t(e)}catch(e){x(e)}})}function x(e){if(console.error){console.error(e)}else if(console.log){console.log("ERROR: ",e)}}function oe(e,t,r){e=s(e);if(r==null){r={}}r["elt"]=e;var n=kt(t,r);if(G.logger&&!Pt(t)){G.logger(e,t,r)}if(r.error){x(r.error);oe(e,"htmx:error",{errorInfo:r})}var i=e.dispatchEvent(n);var a=It(t);if(i&&a!==t){var o=kt(a,n.detail);i=i&&e.dispatchEvent(o)}w(e,function(e){i=i&&e.onEvent(t,n)!==false});return i}var Mt=location.pathname+location.search;function Dt(){var e=K().querySelector("[hx-history-elt],[data-hx-history-elt]");return e||K().body}function Xt(e,t,r,n){if(!D()){return}e=X(e);var i=y(localStorage.getItem("htmx-history-cache"))||[];for(var a=0;aG.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){ae(K().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Ft(e){if(!D()){return null}e=X(e);var t=y(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){oe(K().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Dt();var r=S(t);var n=De(this.response);if(n){var i=b("title");if(i){i.innerHTML=n}else{window.document.title=n}}ke(t,e,r);Wt(r.tasks);Mt=a;oe(K().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{ae(K().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function zt(e){Bt();e=e||location.pathname+location.search;var t=Ft(e);if(t){var r=l(t.content);var n=Dt();var i=S(n);ke(n,r,i);Wt(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Mt=e;oe(K().body,"htmx:historyRestore",{path:e,item:t})}else{if(G.config.refreshOnHistoryMiss){window.location.reload(true)}else{_t(e)}}}function $t(e){var t=he(e,"hx-indicator");if(t==null){t=[e]}te(t,function(e){var t=ee(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,G.config.requestClass)});return t}function Gt(e){te(e,function(e){var t=ee(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,G.config.requestClass)}})}function Jt(e,t){for(var r=0;r=0}function or(e,t){var r=t?t:Y(e,"hx-swap");var n={swapStyle:ee(e).boosted?"innerHTML":G.config.defaultSwapStyle,swapDelay:G.config.defaultSwapDelay,settleDelay:G.config.defaultSettleDelay};if(ee(e).boosted&&!ar(e)){n["show"]="top"}if(r){var i=M(r);if(i.length>0){n["swapStyle"]=i[0];for(var a=1;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}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}if(o.indexOf("focus-scroll:")===0){var d=o.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function sr(e){return Y(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&J(e,"enctype")==="multipart/form-data"}function lr(t,r,n){var i=null;w(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(sr(r)){return rr(n)}else{return tr(n)}}}function S(e){return{tasks:[],elts:[e]}}function ur(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ie(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=ie(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:G.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:G.config.scrollBehavior})}}}function fr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=Z(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=cr(e,function(){return Function("return ("+a+")")()},{})}else{s=y(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return fr(u(e),t,r,n)}function cr(e,t,r){if(G.config.allowEval){return t()}else{ae(e,"htmx:evalDisallowedError");return r}}function hr(e,t){return fr(e,"hx-vars",true,t)}function dr(e,t){return fr(e,"hx-vals",false,t)}function vr(e){return ne(hr(e),dr(e))}function gr(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 pr(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){ae(K().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function E(e,t){return e.getAllResponseHeaders().match(t)}function mr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||A(r,"String")){return se(e,t,null,null,{targetOverride:s(r),returnPromise:true})}else{return se(e,t,s(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:s(r.target),swapOverride:r.swap,returnPromise:true})}}else{return se(e,t,null,null,{returnPromise:true})}}function xr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function se(e,t,n,r,i,M){var a=null;var o=null;i=i!=null?i:{};if(i.returnPromise&&typeof Promise!=="undefined"){var s=new Promise(function(e,t){a=e;o=t})}if(n==null){n=K().body}var D=i.handler||br;if(!re(n)){return}var l=i.targetOverride||ve(n);if(l==null||l==ce){ae(n,"htmx:targetError",{target:Z(n,"hx-target")});return}if(!M){var X=function(){return se(e,t,n,r,i,true)};var F={target:l,elt:n,path:t,verb:e,triggeringEvent:r,etc:i,issueRequest:X};if(oe(n,"htmx:confirm",F)===false){return}}var u=n;var f=ee(n);var c=Y(n,"hx-sync");var h=null;var d=false;if(c){var v=c.split(":");var g=v[0].trim();if(g==="this"){u=de(n,"hx-sync")}else{u=ie(n,g)}c=(v[1]||"drop").trim();f=ee(u);if(c==="drop"&&f.xhr&&f.abortable!==true){return}else if(c==="abort"){if(f.xhr){return}else{d=true}}else if(c==="replace"){oe(u,"htmx:abort")}else if(c.indexOf("queue")===0){var U=c.split(" ");h=(U[1]||"last").trim()}}if(f.xhr){if(f.abortable){oe(u,"htmx:abort")}else{if(h==null){if(r){var p=ee(r);if(p&&p.triggerSpec&&p.triggerSpec.queue){h=p.triggerSpec.queue}}if(h==null){h="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(h==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){se(e,t,n,r,i)})}else if(h==="all"){f.queuedRequests.push(function(){se(e,t,n,r,i)})}else if(h==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){se(e,t,n,r,i)})}return}}var m=new XMLHttpRequest;f.xhr=m;f.abortable=d;var x=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var y=Y(n,"hx-prompt");if(y){var b=prompt(y);if(b===null||!oe(n,"htmx:prompt",{prompt:b,target:l})){Q(a);x();return s}}var w=Y(n,"hx-confirm");if(w){if(!confirm(w)){Q(a);x();return s}}var S=nr(n,l,b);if(i.headers){S=ne(S,i.headers)}var E=Qt(n,e);var C=E.errors;var R=E.values;if(i.values){R=ne(R,i.values)}var B=vr(n);var O=ne(R,B);var q=ir(O,n);if(e!=="get"&&!sr(n)){S["Content-Type"]="application/x-www-form-urlencoded"}if(G.config.getCacheBusterParam&&e==="get"){q["org.htmx.cache-buster"]=J(l,"id")||"true"}if(t==null||t===""){t=K().location.href}var T=fr(n,"hx-request");var V=ee(n).boosted;var H=G.config.methodsThatUseUrlParams.indexOf(e)>=0;var L={boosted:V,useUrlParams:H,parameters:q,unfilteredParameters:O,headers:S,target:l,verb:e,errors:C,withCredentials:i.credentials||T.credentials||G.config.withCredentials,timeout:i.timeout||T.timeout||G.config.timeout,path:t,triggeringEvent:r};if(!oe(n,"htmx:configRequest",L)){Q(a);x();return s}t=L.path;e=L.verb;S=L.headers;q=L.parameters;C=L.errors;H=L.useUrlParams;if(C&&C.length>0){oe(n,"htmx:validation:halted",L);Q(a);x();return s}var j=t.split("#");var W=j[0];var A=j[1];var N=t;if(H){N=W;var _=Object.keys(q).length!==0;if(_){if(N.indexOf("?")<0){N+="?"}else{N+="&"}N+=tr(q);if(A){N+="#"+A}}}m.open(e.toUpperCase(),N,true);m.overrideMimeType("text/html");m.withCredentials=L.withCredentials;m.timeout=L.timeout;if(T.noHeaders){}else{for(var I in S){if(S.hasOwnProperty(I)){var z=S[I];gr(m,I,z)}}}var k={xhr:m,target:l,requestConfig:L,etc:i,boosted:V,pathInfo:{requestPath:t,finalRequestPath:N,anchor:A}};m.onload=function(){try{var e=xr(n);k.pathInfo.responsePath=pr(m);D(n,k);Gt(P);oe(n,"htmx:afterRequest",k);oe(n,"htmx:afterOnLoad",k);if(!re(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(re(r)){t=r}}if(t){oe(t,"htmx:afterRequest",k);oe(t,"htmx:afterOnLoad",k)}}Q(a);x()}catch(e){ae(n,"htmx:onLoadError",ne({error:e},k));throw e}};m.onerror=function(){Gt(P);ae(n,"htmx:afterRequest",k);ae(n,"htmx:sendError",k);Q(o);x()};m.onabort=function(){Gt(P);ae(n,"htmx:afterRequest",k);ae(n,"htmx:sendAbort",k);Q(o);x()};m.ontimeout=function(){Gt(P);ae(n,"htmx:afterRequest",k);ae(n,"htmx:timeout",k);Q(o);x()};if(!oe(n,"htmx:beforeRequest",k)){Q(a);x();return s}var P=$t(n);te(["loadstart","loadend","progress","abort"],function(t){te([m,m.upload],function(e){e.addEventListener(t,function(e){oe(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});oe(n,"htmx:beforeSend",k);var $=H?null:lr(m,n,q);m.send($);return s}function yr(e,t){var r=t.xhr;var n=null;var i=null;if(E(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(E(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(E(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=Y(e,"hx-push-url");var l=Y(e,"hx-replace-url");var u=ee(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 br(l,u){var f=u.xhr;var c=u.target;var e=u.etc;if(!oe(l,"htmx:beforeOnLoad",u))return;if(E(f,/HX-Trigger:/i)){Fe(f,"HX-Trigger",l)}if(E(f,/HX-Location:/i)){Bt();var t=f.getResponseHeader("HX-Location");var h;if(t.indexOf("{")===0){h=y(t);t=h["path"];delete h["path"]}mr("GET",t,h).then(function(){Vt(t)});return}if(E(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");return}if(E(f,/HX-Refresh:/i)){if("true"===f.getResponseHeader("HX-Refresh")){location.reload();return}}if(E(f,/HX-Retarget:/i)){u.target=K().querySelector(f.getResponseHeader("HX-Retarget"))}var d=yr(l,u);var r=f.status>=200&&f.status<400&&f.status!==204;var v=f.response;var n=f.status>=400;var i=ne({shouldSwap:r,serverResponse:v,isError:n},u);if(!oe(c,"htmx:beforeSwap",i))return;c=i.target;v=i.serverResponse;n=i.isError;u.target=c;u.failed=n;u.successful=!n;if(i.shouldSwap){if(f.status===286){Je(l)}w(l,function(e){v=e.transformResponse(v,f,l)});if(d.type){Bt()}var a=e.swapOverride;if(E(f,/HX-Reswap:/i)){a=f.getResponseHeader("HX-Reswap")}var h=or(l,a);c.classList.add(G.config.swappingClass);var g=null;var p=null;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 r;if(E(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}var n=S(c);Xe(h.swapStyle,c,l,v,n,r);if(t.elt&&!re(t.elt)&&t.elt.id){var i=document.getElementById(t.elt.id);var a={preventScroll:h.focusScroll!==undefined?!h.focusScroll:!G.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(G.config.swappingClass);te(n.elts,function(e){if(e.classList){e.classList.add(G.config.settlingClass)}oe(e,"htmx:afterSwap",u)});if(E(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!re(l)){o=K().body}Fe(f,"HX-Trigger-After-Swap",o)}var s=function(){te(n.tasks,function(e){e.call()});te(n.elts,function(e){if(e.classList){e.classList.remove(G.config.settlingClass)}oe(e,"htmx:afterSettle",u)});if(d.type){if(d.type==="push"){Vt(d.path);oe(K().body,"htmx:pushedIntoHistory",{path:d.path})}else{jt(d.path);oe(K().body,"htmx:replacedInHistory",{path:d.path})}}if(u.pathInfo.anchor){var e=b("#"+u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title){var t=b("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}ur(n.elts,h);if(E(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!re(l)){r=K().body}Fe(f,"HX-Trigger-After-Settle",r)}Q(g)};if(h.settleDelay>0){setTimeout(s,h.settleDelay)}else{s()}}catch(e){ae(l,"htmx:swapError",u);Q(p);throw e}};var s=G.config.globalViewTransitions;if(h.hasOwnProperty("transition")){s=h.transition}if(s&&oe(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var m=new Promise(function(e,t){g=e;p=t});var x=o;o=function(){document.startViewTransition(function(){x();return m})}}if(h.swapDelay>0){setTimeout(o,h.swapDelay)}else{o()}}if(n){ae(l,"htmx:responseError",ne({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var wr={};function Sr(){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 Er(e,t){if(t.init){t.init(C)}wr[e]=ne(Sr(),t)}function Cr(e){delete wr[e]}function Rr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=Z(e,"hx-ext");if(t){te(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=wr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Rr(u(e),r,n)}function Or(e){if(K().readyState!=="loading"){e()}else{K().addEventListener("DOMContentLoaded",e)}}function qr(){if(G.config.includeIndicatorStyles!==false){K().head.insertAdjacentHTML("beforeend","")}}function Tr(){var e=K().querySelector('meta[name="htmx-config"]');if(e){return y(e.content)}else{return null}}function Hr(){var e=Tr();if(e){G.config=ne(G.config,e)}}Or(function(){Hr();qr();var e=K().body;Nt(e);var t=K().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ee(t);if(r&&r.xhr){r.xhr.abort()}});var r=window.onpopstate;window.onpopstate=function(e){if(e.state&&e.state.htmx){zt();te(t,function(e){oe(e,"htmx:restored",{document:K(),triggerEvent:oe})})}else{if(r){r(e)}}};setTimeout(function(){oe(e,"htmx:load",{});e=null},0)});return G}()}); \ No newline at end of file From 7a08ae00cd6a7a574efedfaaea9bf921378fd3eb Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:43:33 +1000 Subject: [PATCH 64/80] we can send now? --- templates/answer-post.html | 10 ++++++++++ templates/interactive-posts.html | 12 +----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/templates/answer-post.html b/templates/answer-post.html index 7dd63af..46b1ee4 100644 --- a/templates/answer-post.html +++ b/templates/answer-post.html @@ -21,6 +21,16 @@ send +
+
+
+ + + +
+ + + + {% assets 'css' %} {% endassets %} From 84158454881a6853724233f408d60f2c8be6039f Mon Sep 17 00:00:00 2001 From: Simple_Not <44047940+moonbaseDelta@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:18:33 +1000 Subject: [PATCH 66/80] we can send now? --- app.py | 6 +++--- templates/answer-post.html | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app.py b/app.py index d49ebb4..5fe106d 100644 --- a/app.py +++ b/app.py @@ -180,13 +180,13 @@ def post_to_post(post_key): @app.route("/send_dudes", methods=["POST"]) def upload_file(): + + #### TODO вынести в обычный метод отправки if request.method == "POST": uploaded_file = request.files["file"] - minioClient = Minio( - "static.guaranteedstruggle.host", - ) if uploaded_file: + minioClient = Minio( "static.guaranteedstruggle.host" ) bucket_name = "thread-pics" size = os.fstat(uploaded_file.fileno()).st_size diff --git a/templates/answer-post.html b/templates/answer-post.html index 46b1ee4..41ee36d 100644 --- a/templates/answer-post.html +++ b/templates/answer-post.html @@ -3,10 +3,17 @@
Создай то, о чем будут говорить многие. Ты отвечаешь: >>{{ target_post_id }} >> -
- - -
+
+
+ + +
+
+ + + +
+
@@ -15,14 +22,14 @@ type="submit" name="action" hx-post="/post_to_another_post/{{ target_post_id }}" hx-target="#neu-posts" - hx-include="[name='send_this_text']" + hx-include="[name='send_this_text'], [name='file']" > Отправить!! send
-
+