haori 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ja.md +157 -0
- package/README.md +158 -0
- package/dist/haori.cjs.js +13 -0
- package/dist/haori.cjs.js.map +1 -0
- package/dist/haori.es.js +2929 -0
- package/dist/haori.es.js.map +1 -0
- package/dist/haori.iife.js +13 -0
- package/dist/haori.iife.js.map +1 -0
- package/dist/index.d.ts +824 -0
- package/dist/src/core.d.ts +144 -0
- package/dist/src/core.d.ts.map +1 -0
- package/dist/src/core.js +605 -0
- package/dist/src/core.js.map +1 -0
- package/dist/src/dev.d.ts +35 -0
- package/dist/src/dev.d.ts.map +1 -0
- package/dist/src/dev.js +44 -0
- package/dist/src/dev.js.map +1 -0
- package/dist/src/env.d.ts +25 -0
- package/dist/src/env.d.ts.map +1 -0
- package/dist/src/env.js +64 -0
- package/dist/src/env.js.map +1 -0
- package/dist/src/event.d.ts +144 -0
- package/dist/src/event.d.ts.map +1 -0
- package/dist/src/event.js +221 -0
- package/dist/src/event.js.map +1 -0
- package/dist/src/event_dispatcher.d.ts +50 -0
- package/dist/src/event_dispatcher.d.ts.map +1 -0
- package/dist/src/event_dispatcher.js +99 -0
- package/dist/src/event_dispatcher.js.map +1 -0
- package/dist/src/expression.d.ts +39 -0
- package/dist/src/expression.d.ts.map +1 -0
- package/dist/src/expression.js +148 -0
- package/dist/src/expression.js.map +1 -0
- package/dist/src/form.d.ts +113 -0
- package/dist/src/form.d.ts.map +1 -0
- package/dist/src/form.js +361 -0
- package/dist/src/form.js.map +1 -0
- package/dist/src/fragment.d.ts +427 -0
- package/dist/src/fragment.d.ts.map +1 -0
- package/dist/src/fragment.js +1168 -0
- package/dist/src/fragment.js.map +1 -0
- package/dist/src/haori.d.ts +54 -0
- package/dist/src/haori.d.ts.map +1 -0
- package/dist/src/haori.js +120 -0
- package/dist/src/haori.js.map +1 -0
- package/dist/src/import.d.ts +19 -0
- package/dist/src/import.d.ts.map +1 -0
- package/dist/src/import.js +64 -0
- package/dist/src/import.js.map +1 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/log.d.ts +32 -0
- package/dist/src/log.d.ts.map +1 -0
- package/dist/src/log.js +43 -0
- package/dist/src/log.js.map +1 -0
- package/dist/src/observer.d.ts +17 -0
- package/dist/src/observer.d.ts.map +1 -0
- package/dist/src/observer.js +102 -0
- package/dist/src/observer.js.map +1 -0
- package/dist/src/procedure.d.ts +203 -0
- package/dist/src/procedure.d.ts.map +1 -0
- package/dist/src/procedure.js +1040 -0
- package/dist/src/procedure.js.map +1 -0
- package/dist/src/queue.d.ts +28 -0
- package/dist/src/queue.d.ts.map +1 -0
- package/dist/src/queue.js +150 -0
- package/dist/src/queue.js.map +1 -0
- package/dist/src/url.d.ts +14 -0
- package/dist/src/url.d.ts.map +1 -0
- package/dist/src/url.js +22 -0
- package/dist/src/url.js.map +1 -0
- package/dist/tests/click-attributes.test.d.ts +2 -0
- package/dist/tests/click-attributes.test.d.ts.map +1 -0
- package/dist/tests/click-attributes.test.js +95 -0
- package/dist/tests/click-attributes.test.js.map +1 -0
- package/dist/tests/core.test.d.ts +5 -0
- package/dist/tests/core.test.d.ts.map +1 -0
- package/dist/tests/core.test.js +158 -0
- package/dist/tests/core.test.js.map +1 -0
- package/dist/tests/data-each-browserlike.test.d.ts +2 -0
- package/dist/tests/data-each-browserlike.test.d.ts.map +1 -0
- package/dist/tests/data-each-browserlike.test.js +48 -0
- package/dist/tests/data-each-browserlike.test.js.map +1 -0
- package/dist/tests/data-each-fragment-debug.test.d.ts +2 -0
- package/dist/tests/data-each-fragment-debug.test.d.ts.map +1 -0
- package/dist/tests/data-each-fragment-debug.test.js +119 -0
- package/dist/tests/data-each-fragment-debug.test.js.map +1 -0
- package/dist/tests/data-each-table.test.d.ts +2 -0
- package/dist/tests/data-each-table.test.d.ts.map +1 -0
- package/dist/tests/data-each-table.test.js +63 -0
- package/dist/tests/data-each-table.test.js.map +1 -0
- package/dist/tests/dev.test.d.ts +2 -0
- package/dist/tests/dev.test.d.ts.map +1 -0
- package/dist/tests/dev.test.js +51 -0
- package/dist/tests/dev.test.js.map +1 -0
- package/dist/tests/each_arg.test.d.ts +2 -0
- package/dist/tests/each_arg.test.d.ts.map +1 -0
- package/dist/tests/each_arg.test.js +41 -0
- package/dist/tests/each_arg.test.js.map +1 -0
- package/dist/tests/env.test.d.ts +2 -0
- package/dist/tests/env.test.d.ts.map +1 -0
- package/dist/tests/env.test.js +96 -0
- package/dist/tests/env.test.js.map +1 -0
- package/dist/tests/event.test.d.ts +2 -0
- package/dist/tests/event.test.d.ts.map +1 -0
- package/dist/tests/event.test.js +287 -0
- package/dist/tests/event.test.js.map +1 -0
- package/dist/tests/expression.test.d.ts +2 -0
- package/dist/tests/expression.test.d.ts.map +1 -0
- package/dist/tests/expression.test.js +281 -0
- package/dist/tests/expression.test.js.map +1 -0
- package/dist/tests/fetch-and-procedure-scenarios.test.d.ts +2 -0
- package/dist/tests/fetch-and-procedure-scenarios.test.d.ts.map +1 -0
- package/dist/tests/fetch-and-procedure-scenarios.test.js +133 -0
- package/dist/tests/fetch-and-procedure-scenarios.test.js.map +1 -0
- package/dist/tests/form.test.d.ts +2 -0
- package/dist/tests/form.test.d.ts.map +1 -0
- package/dist/tests/form.test.js +607 -0
- package/dist/tests/form.test.js.map +1 -0
- package/dist/tests/fragment.test.d.ts +2 -0
- package/dist/tests/fragment.test.d.ts.map +1 -0
- package/dist/tests/fragment.test.js +164 -0
- package/dist/tests/fragment.test.js.map +1 -0
- package/dist/tests/import.test.d.ts +2 -0
- package/dist/tests/import.test.d.ts.map +1 -0
- package/dist/tests/import.test.js +148 -0
- package/dist/tests/import.test.js.map +1 -0
- package/dist/tests/log.test.d.ts +2 -0
- package/dist/tests/log.test.d.ts.map +1 -0
- package/dist/tests/log.test.js +58 -0
- package/dist/tests/log.test.js.map +1 -0
- package/dist/tests/procedure-action-operations.test.d.ts +2 -0
- package/dist/tests/procedure-action-operations.test.d.ts.map +1 -0
- package/dist/tests/procedure-action-operations.test.js +148 -0
- package/dist/tests/procedure-action-operations.test.js.map +1 -0
- package/dist/tests/procedure-fetch-options.test.d.ts +2 -0
- package/dist/tests/procedure-fetch-options.test.d.ts.map +1 -0
- package/dist/tests/procedure-fetch-options.test.js +131 -0
- package/dist/tests/procedure-fetch-options.test.js.map +1 -0
- package/dist/tests/procedure.test.d.ts +2 -0
- package/dist/tests/procedure.test.d.ts.map +1 -0
- package/dist/tests/procedure.test.js +136 -0
- package/dist/tests/procedure.test.js.map +1 -0
- package/dist/tests/procedure_events.test.d.ts +7 -0
- package/dist/tests/procedure_events.test.d.ts.map +1 -0
- package/dist/tests/procedure_events.test.js +96 -0
- package/dist/tests/procedure_events.test.js.map +1 -0
- package/dist/tests/reset_each.test.d.ts +2 -0
- package/dist/tests/reset_each.test.d.ts.map +1 -0
- package/dist/tests/reset_each.test.js +215 -0
- package/dist/tests/reset_each.test.js.map +1 -0
- package/dist/tests/row-move.test.d.ts +2 -0
- package/dist/tests/row-move.test.d.ts.map +1 -0
- package/dist/tests/row-move.test.js +197 -0
- package/dist/tests/row-move.test.js.map +1 -0
- package/dist/tests/row-operations.test.d.ts +2 -0
- package/dist/tests/row-operations.test.d.ts.map +1 -0
- package/dist/tests/row-operations.test.js +238 -0
- package/dist/tests/row-operations.test.js.map +1 -0
- package/dist/tests/url.test.d.ts +2 -0
- package/dist/tests/url.test.d.ts.map +1 -0
- package/dist/tests/url.test.js +150 -0
- package/dist/tests/url.test.js.map +1 -0
- package/dist/vite.config.d.ts +3 -0
- package/dist/vite.config.d.ts.map +1 -0
- package/dist/vite.config.js +28 -0
- package/dist/vite.config.js.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +19 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kazuyuki Nishijima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Haori.js(日本語ドキュメント)
|
|
2
|
+
|
|
3
|
+
Haori.js は、HTML 属性を中心にして動的な UI を実現する軽量なライブラリです。JavaScript をほとんど書かずに、データバインディング、条件分岐、繰り返し処理、フォームの双方向バインディング、サーバー通信などを HTML 属性で宣言できます。
|
|
4
|
+
|
|
5
|
+
バージョン: 0.1.0
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**目次**
|
|
10
|
+
|
|
11
|
+
- 概要
|
|
12
|
+
- インストール
|
|
13
|
+
- クイックスタート
|
|
14
|
+
- よく使う属性(概要)
|
|
15
|
+
- 公開・ビルド手順
|
|
16
|
+
- ライセンス・貢献
|
|
17
|
+
- 詳細ドキュメント
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 概要
|
|
22
|
+
|
|
23
|
+
- 設計思想: HTML-First(HTML 属性のみで UI を宣言)
|
|
24
|
+
- 主な特徴:
|
|
25
|
+
- データバインディング(`data-bind`)
|
|
26
|
+
- 条件表示(`data-if`)
|
|
27
|
+
- 繰り返し表示(`data-each`)
|
|
28
|
+
- フォーム双方向バインディング(`name` 属性による自動バインド)
|
|
29
|
+
- サーバー通信(`data-fetch`)
|
|
30
|
+
- HTML インポート(`data-import`)
|
|
31
|
+
- ゼロ依存(ブラウザネイティブのみ)
|
|
32
|
+
|
|
33
|
+
## インストール
|
|
34
|
+
|
|
35
|
+
npm:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install haori
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
CDN:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script src="https://cdn.jsdelivr.net/npm/haori@0.1.0/dist/haori.iife.js"></script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
ES Module:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import Haori from 'haori'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## クイックスタート
|
|
56
|
+
|
|
57
|
+
HTML だけで簡単に使えます。以下は最小の例です。
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html lang="ja">
|
|
62
|
+
<head>
|
|
63
|
+
<meta charset="utf-8">
|
|
64
|
+
<title>Haori サンプル</title>
|
|
65
|
+
<script src="https://cdn.jsdelivr.net/npm/haori@0.1.0/dist/haori.iife.js"></script>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div data-bind='{"name":"太郎"}'>
|
|
69
|
+
<p>こんにちは、{{name}} さん</p>
|
|
70
|
+
</div>
|
|
71
|
+
</body>
|
|
72
|
+
</html>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
JavaScript からマウントする例:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
import Haori from 'haori'
|
|
79
|
+
|
|
80
|
+
Haori.mount(document.body, { items: [ { name: 'りんご' }, { name: 'みかん' } ] })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## よく使う属性(概要)
|
|
86
|
+
|
|
87
|
+
- `data-bind` — 要素にバインディングデータを設定(JSON またはパラメータ形式)
|
|
88
|
+
- `{{ ... }}` — テンプレート式(式評価により挿入)
|
|
89
|
+
- `data-if` — 条件に応じて要素を表示 / 非表示
|
|
90
|
+
- `data-each` — 配列を繰り返し表示(`data-each-key`, `data-each-arg`, `data-each-index` など)
|
|
91
|
+
- `data-fetch` — サーバーからデータを取得してバインド
|
|
92
|
+
- `data-import` — 外部 HTML を読み込んで挿入
|
|
93
|
+
- `data-url-param` — URL のクエリパラメータをバインディングに取り込む
|
|
94
|
+
|
|
95
|
+
詳しい使い方や多数のサンプルについては、公式ドキュメントを参照してください。
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 公開・ビルド手順(パッケージ作成)
|
|
100
|
+
|
|
101
|
+
開発環境でのビルドと公開の基本手順を示します。
|
|
102
|
+
|
|
103
|
+
1. 依存インストール
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
2. 型チェックとビルド
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run compile
|
|
113
|
+
# または
|
|
114
|
+
npm run build
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. テスト
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm run test
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
4. バージョン更新
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm version patch
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
5. npm ログインおよび公開
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm login
|
|
133
|
+
npm publish --access public
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
注意: `package.json` の `name`, `version`, `description`, `repository`, `license` が正しいことを確認してください。公開対象ファイルは `files` フィールドおよび `.npmignore` に従います。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## ライセンス・貢献
|
|
141
|
+
|
|
142
|
+
- ライセンス: MIT(リポジトリの `LICENSE` を参照)
|
|
143
|
+
|
|
144
|
+
貢献歓迎: バグ報告、改善提案、プルリクエストは GitHub リポジトリへお願いします。
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 詳細ドキュメント
|
|
149
|
+
|
|
150
|
+
より詳しい使い方、属性仕様、内部設計については以下のドキュメントを参照してください。
|
|
151
|
+
|
|
152
|
+
- `docs/ja/guide.md` — 利用ガイド(サンプル多数)
|
|
153
|
+
- `docs/ja/specs.md` — 技術仕様書(内部設計・API など)
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
README の作成にあたって追加してほしい項目(API 参照、図、例など)があれば教えてください。
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Haori.js
|
|
2
|
+
|
|
3
|
+
Haori.js is a lightweight, HTML-first UI library that enables dynamic user interfaces primarily through HTML attributes. It lets you declare data bindings, conditional rendering, list rendering, form two-way binding, server fetches, and HTML imports without writing much JavaScript.
|
|
4
|
+
|
|
5
|
+
Version: 0.1.0
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Contents
|
|
10
|
+
|
|
11
|
+
- Overview
|
|
12
|
+
- Installation
|
|
13
|
+
- Quick start
|
|
14
|
+
- Common attributes (summary)
|
|
15
|
+
- Build & publish
|
|
16
|
+
- License & contributing
|
|
17
|
+
- Further documentation
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
- Design principle: HTML-first — declare UI behavior with HTML attributes
|
|
24
|
+
- Key features:
|
|
25
|
+
- Data binding via `data-bind`
|
|
26
|
+
- Conditional rendering via `data-if`
|
|
27
|
+
- List rendering via `data-each`
|
|
28
|
+
- Two-way form binding (automatic binding based on `name` attributes)
|
|
29
|
+
- Server fetches via `data-fetch`
|
|
30
|
+
- HTML imports via `data-import`
|
|
31
|
+
- Zero runtime dependencies (uses browser-native APIs)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Install from npm:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install haori
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Via CDN:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script src="https://cdn.jsdelivr.net/npm/haori@0.1.0/dist/haori.iife.js"></script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
ES Module import:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import Haori from 'haori'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
You can use Haori with plain HTML. Minimal example:
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<head>
|
|
63
|
+
<meta charset="utf-8">
|
|
64
|
+
<title>Haori Sample</title>
|
|
65
|
+
<script src="https://cdn.jsdelivr.net/npm/haori@0.1.0/dist/haori.iife.js"></script>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div data-bind='{"name":"Taro"}'>
|
|
69
|
+
<p>Hello, {{name}}</p>
|
|
70
|
+
</div>
|
|
71
|
+
</body>
|
|
72
|
+
</html>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Mounting from JavaScript:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
import Haori from 'haori'
|
|
79
|
+
|
|
80
|
+
Haori.mount(document.body, { items: [ { name: 'apple' }, { name: 'orange' } ] })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Common attributes (summary)
|
|
86
|
+
|
|
87
|
+
- `data-bind` — set binding data for an element (JSON or parameter format)
|
|
88
|
+
- `{{ ... }}` — template expressions (evaluated and inserted)
|
|
89
|
+
- `data-if` — show/hide an element based on a condition
|
|
90
|
+
- `data-each` — repeat an element for items in an array (`data-each-key`, `data-each-arg`, `data-each-index`)
|
|
91
|
+
- `data-fetch` — fetch data from a server and bind the result
|
|
92
|
+
- `data-import` — load external HTML and insert it
|
|
93
|
+
- `data-url-param` — import URL query parameters into bindings
|
|
94
|
+
|
|
95
|
+
For detailed usage and many examples, see the official documentation.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Build & publish (packaging)
|
|
100
|
+
|
|
101
|
+
Basic build and publish steps in a development environment:
|
|
102
|
+
|
|
103
|
+
1. Install dependencies
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
2. Type-check and build
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run compile
|
|
113
|
+
# or
|
|
114
|
+
npm run build
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. Run tests
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm run test
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
4. Bump version
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm version patch
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
5. Login to npm and publish
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm login
|
|
133
|
+
npm publish --access public
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Make sure `package.json` fields `name`, `version`, `description`, `repository` and `license` are correct. Files published to npm are controlled by the `files` field in `package.json` and `.npmignore`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## License & Contributing
|
|
141
|
+
|
|
142
|
+
- License: MIT (see `LICENSE` in this repository)
|
|
143
|
+
|
|
144
|
+
Contributions are welcome — please open issues or pull requests on the GitHub repository.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Further documentation
|
|
149
|
+
|
|
150
|
+
For more detailed usage, attribute specs, and internal design, see:
|
|
151
|
+
|
|
152
|
+
- `docs/ja/guide.md` — User guide (many examples)
|
|
153
|
+
- `docs/ja/specs.md` — Technical specifications (internal design, API)
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
If you would like additional sections (API reference, diagrams, more examples), tell me what to include and I will expand the README.
|
|
158
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const C=class C{static isEnabled(){return C.devMode}static enable(){C.devMode=!0}static disable(){C.devMode=!1}static set(t){C.devMode=t}};C.devMode=!1;let D=C;const k=class k{static detect(){try{const t=document.currentScript||document.querySelector('script[src*="haori"]');if(t instanceof HTMLScriptElement){const s=t.getAttribute("data-prefix")||k._prefix;k._prefix=s.endsWith("-")?s:s+"-"}if(t instanceof HTMLScriptElement&&t.hasAttribute(`${k._prefix}dev`)){D.set(!0);return}const e=window.location.hostname;if(e==="localhost"||e.endsWith(".localhost")||e==="127.0.0.1"||e==="::1"||e.endsWith(".local")){D.set(!0);return}D.set(!1)}catch{}}static get prefix(){return k._prefix}};k._prefix="data-";let c=k;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",c.detect):c.detect();class h{static info(t,...e){D.isEnabled()&&console.log&&console.log(t,...e)}static warn(t,...e){D.isEnabled()&&console.warn&&console.warn(t,...e)}static error(t,...e){console.error(t,...e)}}class X{constructor(){this.MAX_BUDGET=8,this.queue=[],this.processing=!1}enqueue(t,e=!1){let s,i;const r=new Promise((a,u)=>{s=a,i=u}),n={task:t,timestamp:performance.now(),promise:r,resolve:s,reject:i};return e?this.queue.unshift(n):this.queue.push(n),this.scheduleProcessing(),r}async processQueue(){if(!(this.processing||this.queue.length===0)){this.processing=!0;try{const t=performance.now();for(;this.queue.length>0;){const e=this.queue.shift();if(!e)return;try{const s=await e.task();e.resolve(s)}catch(s){e.reject(s),h.error("[Haori]",`Task ${e.timestamp} failed:`,s)}if(performance.now()-t>this.MAX_BUDGET)break}}catch(t){h.error("[Haori]","Error processing queue:",t)}finally{this.processing=!1,this.queue.length>0&&this.scheduleProcessing()}}}scheduleProcessing(){this.processing||(typeof requestAnimationFrame<"u"?requestAnimationFrame(()=>{this.processQueue()}):setTimeout(()=>{this.processQueue()},16))}async wait(){if(this.queue.length===0&&!this.processing)return;const t=this.queue.map(e=>e.promise);t.length>0&&await Promise.allSettled(t)}}const K=class K{static enqueue(t,e=!1){return this.ASYNC_QUEUE.enqueue(t,e)}static wait(){return this.ASYNC_QUEUE.wait()}};K.ASYNC_QUEUE=new X;let v=K;class M{static dialog(t){return v.enqueue(()=>{window.alert(t)},!0)}static async toast(t,e){const s=document.createElement("div");s.className=`haori-toast haori-toast-${e}`,s.textContent=t,s.setAttribute("popover","manual"),s.setAttribute("role","status"),s.setAttribute("aria-live","polite"),document.body.appendChild(s),s.showPopover(),setTimeout(()=>{try{s.hidePopover()}finally{s.remove()}},3e3)}static confirm(t){return v.enqueue(()=>window.confirm(t),!0)}static openDialog(t){return v.enqueue(()=>{t instanceof HTMLDialogElement?t.showModal():h.error("[Haori]","Element is not a dialog: ",t)},!0)}static closeDialog(t){return v.enqueue(()=>{t instanceof HTMLDialogElement?t.close():h.error("[Haori]","Element is not a dialog: ",t)},!0)}static addErrorMessage(t,e){return v.enqueue(()=>{if(t instanceof HTMLFormElement){t.setAttribute("data-message",e);return}if(t.parentElement){t.parentElement.setAttribute("data-message",e);return}t.setAttribute("data-message",e)},!0)}static clearMessages(t){return v.enqueue(()=>{t.removeAttribute("data-message"),t.querySelectorAll("[data-message]").forEach(e=>{e.removeAttribute("data-message")})},!0)}}class m{static getValues(t){const e={};return m.getPartValues(t,e)}static getPartValues(t,e){const s=t.getAttribute("name"),i=t.getAttribute(`${c.prefix}form-object`),r=t.getAttribute(`${c.prefix}form-list`);if(s){r?Array.isArray(e[String(s)])?e[String(s)].push(t.getValue()):e[String(s)]=[t.getValue()]:e[String(s)]=t.getValue(),i&&h.warn("Haori",`Element cannot have both ${c.prefix}form-object and name attributes.`);for(const n of t.getChildElementFragments())m.getPartValues(n,e)}else if(i){const n={};for(const a of t.getChildElementFragments())m.getPartValues(a,n);Object.keys(n).length>0&&(e[String(i)]=n),r&&h.warn("Haori",`Element cannot have both ${c.prefix}form-list and ${c.prefix}form-object attributes.`)}else if(r){const n=[];for(const a of t.getChildElementFragments()){const u={};m.getPartValues(a,u),Object.keys(u).length>0&&n.push(u)}n.length>0&&(e[String(r)]=n)}else for(const n of t.getChildElementFragments())m.getPartValues(n,e);return e}static setValues(t,e,s=!1){return m.setPartValues(t,e,null,s)}static setPartValues(t,e,s=null,i=!1){const r=[],n=t.getAttribute("name"),a=t.getAttribute(`${c.prefix}form-object`),u=t.getAttribute(`${c.prefix}form-list`),b=t.getAttribute(`${c.prefix}form-detach`);if(n){if(!b||i){const l=e[String(n)];u&&Array.isArray(l)&&s!==null?r.push(t.setValue(l[s])):typeof l=="string"||typeof l=="number"||typeof l=="boolean"||l===null?r.push(t.setValue(l)):r.push(t.setValue(String(l)))}}else if(a){const l=e[String(a)];if(l&&typeof l=="object")for(const E of t.getChildElementFragments())r.push(m.setPartValues(E,l,null,i))}else if(u){const l=e[String(u)];if(Array.isArray(l)){const E=t.getChildElementFragments();for(let o=0;o<E.length;o++){const d=E[o];l.length>o?r.push(m.setPartValues(d,l[o],o,i)):r.push(m.setPartValues(d,{},o,i))}}}else for(const l of t.getChildElementFragments())r.push(m.setPartValues(l,e,null,i));return Promise.all(r).then(()=>{})}static async reset(t){m.clearValues(t),await Promise.all([m.clearMessages(t),m.clearEachClones(t)]),await v.enqueue(()=>{const e=t.getTarget();if(e instanceof HTMLFormElement)e.reset();else{const s=e.parentElement;if(s){const i=e.nextElementSibling,r=document.createElement("form");r.appendChild(e),r.reset(),s.insertBefore(e,i)}}}),await N.evaluateAll(t)}static clearEachClones(t){const e=[],s=r=>{if(r.hasAttribute(`${c.prefix}each`))for(const n of r.getChildElementFragments()){const a=n.hasAttribute(`${c.prefix}each-before`),u=n.hasAttribute(`${c.prefix}each-after`);!a&&!u&&e.push(n.remove())}},i=r=>{s(r);for(const n of r.getChildElementFragments())i(n)};s(t);for(const r of t.getChildElementFragments())i(r);return Promise.all(e).then(()=>{})}static clearValues(t){t.clearValue();for(const e of t.getChildElementFragments())m.clearValues(e)}static clearMessages(t){return M.clearMessages(t.getTarget())}static addErrorMessage(t,e,s){const i=[],r=m.findFragmentsByKey(t,e);return r.forEach(n=>{i.push(M.addErrorMessage(n.getTarget(),s))}),r.length===0&&i.push(M.addErrorMessage(t.getTarget(),s)),Promise.all(i).then(()=>{})}static findFragmentsByKey(t,e){return m.findFragmentByKeyParts(t,e.split("."))}static findFragmentByKeyParts(t,e){const s=[],i=e[0];if(e.length==1&&t.getAttribute("name")===i&&s.push(t),t.hasAttribute(`${c.prefix}form-object`))e.length>1&&t.getAttribute(`${c.prefix}form-object`)===i&&t.getChildElementFragments().forEach(n=>{s.push(...m.findFragmentByKeyParts(n,e.slice(1)))});else if(t.hasAttribute(`${c.prefix}form-list`)){if(e.length>1){const r=t.getAttribute(`${c.prefix}form-list`),n=i.lastIndexOf("["),a=i.lastIndexOf("]");if(n!==-1&&a!==-1&&n<a){const u=i.substring(0,n);if(r===u){const b=i.substring(n+1,a),l=Number(b);if(isNaN(l))h.error("Haori",`Invalid index: ${i}`);else{const E=t.getChildElementFragments().filter(o=>o.hasAttribute(`${c.prefix}row`));l<E.length&&s.push(...m.findFragmentByKeyParts(E[l],e.slice(1)))}}}}}else t.getChildElementFragments().forEach(r=>{s.push(...m.findFragmentByKeyParts(r,e))});return s}static getFormFragment(t){if(t.getTarget()instanceof HTMLFormElement)return t;const s=t.getParent();return s?this.getFormFragment(s):null}}const P=class P{static evaluate(t,e={}){if(t.trim()==="")return h.warn("[Haori]",t,"Expression is empty"),null;if(this.containsDangerousPatterns(t))return h.warn("[Haori]",t,"Expression contains dangerous patterns"),null;if(this.containsForbiddenKeys(e))return h.warn("[Haori]",e,"Binded values contain forbidden keys"),null;const s=Object.keys(e).filter(n=>!this.FORBIDDEN_NAMES.includes(n)&&!this.STRICT_FORBIDDEN_NAMES.includes(n)).sort(),i=`${t}:${s.join(",")}`;let r=this.EXPRESSION_CACHE.get(i);if(!r){const n=`"use strict";
|
|
2
|
+
${this.assignments};
|
|
3
|
+
return (${t});`;try{r=new Function(...s,n),this.EXPRESSION_CACHE.set(i,r)}catch(a){return h.error("[Haori]","Failed to compile expression:",t,a),null}}try{const n=[];return s.forEach(a=>{n.push(e[a])}),r(...n)}catch(n){return h.error("[Haori]","Expression evaluation error:",t,n),n instanceof ReferenceError?void 0:null}}static containsDangerousPatterns(t){return[/\beval\s*\(/,/\barguments\s*\[/,/\barguments\s*\./].some(s=>s.test(t))}static containsForbiddenKeys(t){if(t&&typeof t=="object"){for(const e of Object.keys(t))if(this.FORBIDDEN_NAMES.includes(e)||this.STRICT_FORBIDDEN_NAMES.includes(e)||this.containsForbiddenKeys(t[e]))return!0}return!1}};P.FORBIDDEN_NAMES=["window","self","globalThis","frames","parent","top","Function","setTimeout","setInterval","requestAnimationFrame","alert","confirm","prompt","fetch","XMLHttpRequest","constructor","__proto__","prototype","Object","document","location","navigator","localStorage","sessionStorage","IndexedDB","history"],P.STRICT_FORBIDDEN_NAMES=["eval","arguments"],P.EXPRESSION_CACHE=new Map,(()=>{const t=[];P.FORBIDDEN_NAMES.forEach(e=>{t.push(`const ${e} = undefined`)}),P.assignments=t.join(`;
|
|
4
|
+
`)})();let L=P;const R=class R{constructor(t){this.parent=null,this.mounted=!1,this.skipMutationNodes=!1,this.target=t,R.FRAGMENT_CACHE.set(t,this)}static get(t){if(t==null)return null;if(R.FRAGMENT_CACHE.has(t))return R.FRAGMENT_CACHE.get(t);let e;switch(t.nodeType){case Node.ELEMENT_NODE:e=new F(t);break;case Node.TEXT_NODE:e=new j(t);break;case Node.COMMENT_NODE:e=new U(t);break;default:return h.warn("[Haori]","Unsupported node type:",t.nodeType),null}return e}isSkipMutationNodes(){return this.skipMutationNodes}unmount(){if(!this.mounted||this.skipMutationNodes)return Promise.resolve();if(this.parent){const t=this.parent,e=t.skipMutationNodes;return v.enqueue(()=>{t.skipMutationNodes=!0,this.target.parentNode===t.getTarget()&&t.getTarget().removeChild(this.target),this.mounted=!1}).finally(()=>{t.skipMutationNodes=e})}else{const t=this.target.parentNode;if(t)return v.enqueue(()=>{this.target.parentNode===t&&t.removeChild(this.target),this.mounted=!1});this.mounted=!1}return Promise.resolve()}mount(){if(this.mounted||this.skipMutationNodes)return Promise.resolve();if(this.parent){const t=this.parent,e=t.skipMutationNodes;return v.enqueue(()=>{t.skipMutationNodes=!0,this.target.parentNode!==t.getTarget()&&t.getTarget().appendChild(this.target),this.mounted=!0}).finally(()=>{t.skipMutationNodes=e})}return Promise.resolve()}isMounted(){return this.mounted}setMounted(t){this.mounted=t}remove(t=!0){return this.parent&&this.parent.removeChild(this),R.FRAGMENT_CACHE.delete(this.target),t?this.unmount():Promise.resolve()}getTarget(){return this.target}getParent(){return this.parent}setParent(t){this.parent=t}};R.FRAGMENT_CACHE=new WeakMap;let A=R;class F extends A{constructor(t){super(t),this.INPUT_EVENT_TYPES=["text","password","email","url","tel","search","number","range","color","date","datetime-local","month","time","week"],this.children=[],this.attributeMap=new Map,this.bindingData=null,this.bindingDataCache=null,this.visible=!0,this.display=null,this.template=null,this.listKey=null,this.value=null,this.skipMutationAttributes=!1,this.skipChangeValue=!1,this.syncValue(),t.getAttributeNames().forEach(e=>{const s=t.getAttribute(e);if(s!==null&&!this.attributeMap.has(e)){const i=new I(e,s);this.attributeMap.set(e,i)}}),t.childNodes.forEach(e=>{const s=A.get(e);s.setParent(this),this.children.push(s)})}getChildren(){return this.children}getChildElementFragments(){return this.children.filter(t=>t instanceof F)}pushChild(t){this.children.push(t),t.setParent(this)}removeChild(t){const e=this.children.indexOf(t);if(e<0){h.warn("[Haori]","Child fragment not found.",t);return}this.children.splice(e,1),t.setParent(null)}clone(){const t=new F(this.target.cloneNode(!1));return this.children.forEach(e=>{const s=e.clone();t.getTarget().appendChild(s.getTarget()),t.pushChild(s)}),t.mounted=!1,t.bindingData=this.bindingData,t.clearBindingDataCache(),t.visible=this.visible,t.display=this.display,t.template=this.template,t}remove(t=!0){const e=[];return this.children.forEach(s=>{e.push(s.remove(!1))}),this.children.length=0,this.attributeMap.clear(),this.bindingData=null,this.bindingDataCache=null,this.template&&(e.push(this.template.remove(!1)),this.template=null),e.push(super.remove(t)),Promise.all(e).then(()=>{})}getTarget(){return this.target}getBindingData(){return this.bindingDataCache?this.bindingDataCache:(this.bindingDataCache={},this.parent&&Object.assign(this.bindingDataCache,this.parent.getBindingData()),this.bindingData&&Object.assign(this.bindingDataCache,this.bindingData),this.bindingDataCache)}getRawBindingData(){return this.bindingData}setBindingData(t){this.bindingData=t,this.clearBindingDataCache()}clearBindingDataCache(){this.bindingDataCache=null,this.children.forEach(t=>{t instanceof F&&t.clearBindingDataCache()})}getTemplate(){return this.template}setTemplate(t){this.template=t}setListKey(t){this.listKey=t}getListKey(){return this.listKey}setValue(t){if(this.skipChangeValue||this.value===t)return Promise.resolve();const e=this.getTarget();if(e instanceof HTMLInputElement&&(e.type==="checkbox"||e.type==="radio")){const s=this.getAttribute("value");let i;return s==="true"?i=t===!0:s==="false"?i=t===!1:i=s===String(t),this.value=i?t:null,e.checked===i?Promise.resolve():(this.skipChangeValue=!0,v.enqueue(()=>{e.checked=i,e.dispatchEvent(new Event("change",{bubbles:!0}))}).finally(()=>{this.skipChangeValue=!1}))}else return e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement?(this.value=t,this.skipChangeValue=!0,v.enqueue(()=>{e.value=t===null?"":String(t),(e instanceof HTMLInputElement&&this.INPUT_EVENT_TYPES.includes(e.type)||e instanceof HTMLTextAreaElement)&&e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0}))}).finally(()=>{this.skipChangeValue=!1})):(h.warn("[Haori]","setValue is not supported for this element type.",e),Promise.resolve())}getValue(){return this.value}clearValue(){this.value=null}syncValue(){const t=this.getTarget();if(t instanceof HTMLInputElement)if(t.type==="checkbox"||t.type==="radio")if(t.checked){const e=t.value;e==="true"?this.value=!0:e==="false"?this.value=!1:this.value=e}else{const e=t.value;e==="true"?this.value=!1:e==="false"?this.value=!0:this.value=null}else this.value=t.value;else t instanceof HTMLTextAreaElement?this.value=t.value:t instanceof HTMLSelectElement&&(this.value=t.value)}setAttribute(t,e){if(this.skipMutationAttributes)return Promise.resolve();if(e===null)return this.removeAttribute(t);const s=new I(t,e);this.attributeMap.set(t,s),this.skipMutationAttributes=!0;const i=this.getTarget(),r=s.isForceEvaluation()?e:this.getAttribute(t);return v.enqueue(()=>{if(r===null||r===!1)i.removeAttribute(t);else{const n=String(r);i.getAttribute(t)!==n&&i.setAttribute(t,n)}}).finally(()=>{this.skipMutationAttributes=!1})}removeAttribute(t){if(this.skipMutationAttributes)return Promise.resolve();this.attributeMap.delete(t),this.skipMutationAttributes=!0;const e=this.getTarget();return v.enqueue(()=>{e.removeAttribute(t)}).finally(()=>{this.skipMutationAttributes=!1})}getAttribute(t){const e=this.attributeMap.get(t);if(e===void 0)return null;const s=e.evaluate(this.getBindingData());return s.length===1?s[0]:$.joinEvaluateResults(s)}getRawAttribute(t){const e=this.attributeMap.get(t);return e===void 0?null:e.getValue()}getAttributeNames(){return Array.from(this.attributeMap.keys())}hasAttribute(t){return this.attributeMap.has(t)}insertBefore(t,e){if(this.skipMutationNodes)return Promise.resolve();if(t===this)return h.error("[Haori]","Cannot insert element as child of itself"),Promise.reject(new Error("Self-insertion not allowed"));const s=new Set;let i=this.parent;for(;i;)s.add(i),i=i.getParent();if(s.has(t))return h.error("[Haori]","Cannot create circular reference"),Promise.reject(new Error("Circular reference detected"));const r=t.getParent()===this;let n=-1,a=-1;r&&(n=this.children.indexOf(t),e!==null&&(a=this.children.indexOf(e)));const u=t.getParent();if(u!==null&&u.removeChild(t),e===null)this.children.push(t);else{let l;r?n!==-1&&n<a?l=a-1:l=a:l=this.children.indexOf(e),l===-1?(h.warn("[Haori]","Reference child not found in children.",e),this.children.push(t)):this.children.splice(l,0,t)}t.setParent(this),t.setMounted(this.mounted);const b=this.skipMutationNodes;return this.skipMutationNodes=!0,v.enqueue(()=>{this.target.insertBefore(t.getTarget(),e?.getTarget()||null)}).finally(()=>{this.skipMutationNodes=b})}insertAfter(t,e){if(e==null)return this.insertBefore(t,null);const s=this.children.indexOf(e);return s===-1?(h.warn("[Haori]","Reference child not found in children.",e),this.insertBefore(t,null)):this.insertBefore(t,this.children[s+1]||null)}getPrevious(){const t=this.getParent();if(t===null)return null;const e=t.getChildElementFragments(),s=e.indexOf(this);return s<=0?null:e[s-1]}getNext(){const t=this.getParent();if(t===null)return null;const e=t.getChildElementFragments(),s=e.indexOf(this);return s<0||s+1>=e.length?null:e[s+1]}isVisible(){return this.visible}hide(){return this.visible=!1,this.display=this.getTarget().style.display,this.getTarget().style.display="none",this.getTarget().setAttribute(`${c.prefix}if-false`,""),Promise.resolve()}show(){return this.getTarget().style.display=this.display??"",this.getTarget().removeAttribute(`${c.prefix}if-false`),this.visible=!0,Promise.resolve()}closestByAttribute(t){if(this.hasAttribute(t))return this;const e=this.getParent();return e===null?null:e.closestByAttribute(t)}}class j extends A{constructor(t){super(t),this.skipMutation=!1,this.text=t.textContent||"",this.contents=new $(this.text)}clone(){const t=new j(this.target.cloneNode(!0));return t.mounted=!1,t.text=this.text,t.contents=this.contents,t}getTarget(){return this.target}setContent(t){return this.skipMutation||this.text===t?Promise.resolve():(this.text=t,this.contents=new $(t),this.evaluate())}evaluate(){return this.contents.isRawEvaluate&&this.parent===null?Promise.reject(new Error("Parent fragment is required for raw evaluation")):v.enqueue(()=>{this.skipMutation=!0,this.contents.isRawEvaluate?this.parent.getTarget().innerHTML=this.contents.evaluate(this.parent.getBindingData())[0]:this.contents.isEvaluate?this.target.textContent=$.joinEvaluateResults(this.contents.evaluate(this.parent.getBindingData())):this.target.textContent=this.text}).finally(()=>{this.skipMutation=!1})}}class U extends A{constructor(t){super(t),this.skipMutation=!1,this.text=t.textContent||""}clone(){const t=new U(this.target.cloneNode(!0));return t.mounted=!1,t.text=this.text,t}getTarget(){return this.target}setContent(t){return this.skipMutation||this.text===t?Promise.resolve():(this.text=t,v.enqueue(()=>{this.skipMutation=!0,this.target.textContent=this.text}).finally(()=>{this.skipMutation=!1}))}}const V=class V{constructor(t){this.contents=[],this.isEvaluate=!1,this.isRawEvaluate=!1,this.value=t;const e=[...t.matchAll(V.PLACEHOLDER_REGEX)];let s=0,i=!1,r=!1;for(const n of e){n.index>s&&this.contents.push({text:t.slice(s,n.index),type:0});const a={text:n[1]??n[2],type:n[1]?2:1};i=!0,r=r||a.type===2,this.contents.push(a),s=n.index+n[0].length}s<t.length&&this.contents.push({text:t.slice(s),type:0}),this.isEvaluate=i,this.isRawEvaluate=r,this.checkRawExpressions()}static joinEvaluateResults(t){return t===null||t.length===0?"":t.map(e=>e==null||e===!1||Number.isNaN(e)?"":typeof e!="string"?String(e):e).join("")}getValue(){return this.value}checkRawExpressions(){for(let t=0;t<this.contents.length;t++)this.contents[t].type===2&&this.contents.length>1&&(h.error("[Haori]","Raw expressions are not allowed in multi-content expressions."),this.contents[t].type=1)}evaluate(t){if(!this.isEvaluate&&!this.isRawEvaluate)return this.contents.map(s=>s.text);const e=[];return this.contents.forEach(s=>{try{if(s.type===1||s.type===2){const i=L.evaluate(s.text,t);e.push(i)}else e.push(s.text)}catch(i){h.error("[Haori]",`Error evaluating text expression: ${s.text}`,i),e.push("")}}),e}};V.PLACEHOLDER_REGEX=/\{\{\{([\s\S]+?)\}\}\}|\{\{([\s\S]+?)\}\}/g;let $=V;const q=class q extends ${constructor(t,e){super(e),this.forceEvaluation=q.FORCE_EVALUATION_ATTRIBUTES.includes(t)}isForceEvaluation(){return this.forceEvaluation}evaluate(t){if(!this.isEvaluate&&!this.forceEvaluation)return this.contents.map(s=>s.text);const e=[];return this.contents.forEach(s=>{try{if(this.forceEvaluation&&s.type===0||s.type===1||s.type===2){const i=L.evaluate(s.text,t);e.push(i)}else e.push(s.text)}catch(i){h.error("[Haori]",`Error evaluating attribute expression: ${s.text}`,i),e.push("")}}),this.forceEvaluation&&e.length>1?(h.error("[Haori]","each or if expressions must have a single content.",e),[e[0]]):e}};q.FORCE_EVALUATION_ATTRIBUTES=["data-if","hor-if","data-each","hor-each"];let I=q;class y{static dispatch(t,e,s,i){const r=new CustomEvent(`haori:${e}`,{bubbles:i?.bubbles??!0,cancelable:i?.cancelable??!1,composed:i?.composed??!0,detail:s});return t.dispatchEvent(r)}static ready(t){y.dispatch(document,"ready",{version:t})}static render(t){y.dispatch(t,"render",{target:t})}static importStart(t,e){y.dispatch(t,"importstart",{url:e,startedAt:performance.now()})}static importEnd(t,e,s,i){y.dispatch(t,"importend",{url:e,bytes:s,durationMs:performance.now()-i})}static importError(t,e,s){y.dispatch(t,"importerror",{url:e,error:s})}static bindChange(t,e,s,i="other"){const r=[],n=new Set(Object.keys(e||{})),a=new Set(Object.keys(s)),u=new Set([...n,...a]);for(const b of u){const l=e?.[b],E=s[b];l!==E&&r.push(b)}y.dispatch(t,"bindchange",{previous:e||{},next:s,changedKeys:r,reason:i})}static eachUpdate(t,e,s,i){y.dispatch(t,"eachupdate",{added:e,removed:s,order:i,total:i.length})}static rowAdd(t,e,s,i){y.dispatch(t,"rowadd",{key:e,index:s,item:i})}static rowRemove(t,e,s){y.dispatch(t,"rowremove",{key:e,index:s})}static rowMove(t,e,s,i){y.dispatch(t,"rowmove",{key:e,from:s,to:i})}static show(t){y.dispatch(t,"show",{visible:!0})}static hide(t){y.dispatch(t,"hide",{visible:!1})}static fetchStart(t,e,s,i){y.dispatch(t,"fetchstart",{url:e,options:s||{},payload:i,startedAt:performance.now()})}static fetchEnd(t,e,s,i){y.dispatch(t,"fetchend",{url:e,status:s,durationMs:performance.now()-i})}static fetchError(t,e,s,i,r){y.dispatch(t,"fetcherror",{url:e,status:i,error:s,durationMs:r?performance.now()-r:void 0})}}class f{static attrName(t,e,s=!1){return t?`${c.prefix}${t}-${e}`:s?`${c.prefix}fetch-${e}`:`${c.prefix}${e}`}static buildOptions(t,e){const s={targetFragment:t};if(e){if(t.hasAttribute(f.attrName(e,"validate"))&&(s.valid=!0),t.hasAttribute(f.attrName(e,"confirm"))&&(s.confirmMessage=t.getAttribute(f.attrName(e,"confirm"))),t.hasAttribute(f.attrName(e,"data"))&&(s.data=N.parseDataBind(t.getRawAttribute(f.attrName(e,"data")))),t.hasAttribute(f.attrName(e,"form"))){const o=t.getRawAttribute(f.attrName(e,"form"));if(o){const d=document.body.querySelector(o);d!==null?s.formFragment=m.getFormFragment(A.get(d)):h.error("Haori",`Form element not found: ${o} (${f.attrName(e,"form")})`)}else s.formFragment=m.getFormFragment(t)}else e==="change"&&(s.formFragment=m.getFormFragment(t));if(t.hasAttribute(`${c.prefix}${e}-before-run`)){const o=t.getRawAttribute(`${c.prefix}${e}-before-run`);try{s.beforeCallback=new Function("fetchUrl","fetchOptions",`
|
|
5
|
+
"use strict";
|
|
6
|
+
${o}
|
|
7
|
+
`)}catch(d){h.error("Haori",`Invalid before script: ${d}`)}}}const i=f.attrName(e,"fetch"),r=t.hasAttribute(i);r&&(s.fetchUrl=t.getAttribute(i));const n={};if(e){const o=f.attrName(e,"fetch-method");t.hasAttribute(o)&&(n.method=t.getAttribute(o))}else{const o=f.attrName(null,"method",!0);t.hasAttribute(o)&&(n.method=t.getAttribute(o))}if(e){const o=f.attrName(e,"fetch-headers");if(t.hasAttribute(o)){const d=t.getRawAttribute(o);try{n.headers=N.parseDataBind(d)}catch(g){h.error("Haori",`Invalid fetch headers: ${g}`)}}}else{const o=f.attrName(null,"headers",!0);if(t.hasAttribute(o)){const d=t.getRawAttribute(o);try{n.headers=N.parseDataBind(d)}catch(g){h.error("Haori",`Invalid fetch headers: ${g}`)}}}if(e){const o=f.attrName(e,"fetch-content-type");if(t.hasAttribute(o))n.headers={...n.headers,"Content-Type":t.getAttribute(o)};else if(n.method&&n.method!=="GET"&&n.method!=="HEAD"&&n.method!=="OPTIONS"){let d=!1;n.headers&&typeof n.headers=="object"&&(d="Content-Type"in n.headers),d||(n.headers={...n.headers,"Content-Type":"application/json"})}else n.method&&(n.method==="GET"||n.method==="HEAD"||n.method==="OPTIONS")&&(n.headers={...n.headers,"Content-Type":"application/x-www-form-urlencoded"})}else{const o=f.attrName(null,"content-type",!0);if(t.hasAttribute(o))n.headers={...n.headers,"Content-Type":t.getAttribute(o)};else if(n.method&&n.method!=="GET"&&n.method!=="HEAD"&&n.method!=="OPTIONS"){let d=!1;n.headers&&typeof n.headers=="object"&&(d="Content-Type"in n.headers),d||(n.headers={...n.headers,"Content-Type":"application/json"})}else n.method&&(n.method==="GET"||n.method==="HEAD"||n.method==="OPTIONS")&&(n.headers={...n.headers,"Content-Type":"application/x-www-form-urlencoded"})}Object.keys(n).length>0&&(s.fetchOptions=n);const a=e?f.attrName(e,"bind"):f.attrName(null,"bind",!0);if(t.hasAttribute(a)){const o=t.getRawAttribute(a);if(o){const d=document.body.querySelectorAll(o);d.length>0?(s.bindFragments=[],d.forEach(g=>{const w=A.get(g);w&&s.bindFragments.push(w)})):h.error("Haori",`Bind element not found: ${o} (${a})`)}}const u=f.attrName(e,"bind-arg"),b=f.attrName(null,"arg",!0),l=f.attrName(null,"bind-arg",!0);e?t.hasAttribute(u)&&(s.bindArg=t.getRawAttribute(u)):t.hasAttribute(b)?s.bindArg=t.getRawAttribute(b):t.hasAttribute(l)&&(s.bindArg=t.getRawAttribute(l));const E=e?f.attrName(e,"bind-params"):f.attrName(null,"bind-params",!0);if(t.hasAttribute(E)){const o=t.getRawAttribute(E);s.bindParams=o.split("&").map(d=>d.trim())}if(e){if(t.hasAttribute(f.attrName(e,"adjust"))){const d=t.getRawAttribute(f.attrName(e,"adjust"));if(d){const g=document.body.querySelectorAll(d);g.length>0?(s.adjustFragments=[],g.forEach(w=>{const T=A.get(w);T&&s.adjustFragments.push(T)})):h.error("Haori",`Adjust element not found: ${d} (${f.attrName(e,"adjust")})`)}if(t.hasAttribute(f.attrName(e,"adjust-value"))){const g=t.getRawAttribute(f.attrName(e,"adjust-value")),w=Number(g);isNaN(w)||(s.adjustValue=w)}}if(t.hasAttribute(f.attrName(e,"row-add"))&&(s.rowAdd=!0),t.hasAttribute(f.attrName(e,"row-remove"))&&(s.rowRemove=!0),t.hasAttribute(f.attrName(e,"row-prev"))&&(s.rowMovePrev=!0),t.hasAttribute(f.attrName(e,"row-next"))&&(s.rowMoveNext=!0),t.hasAttribute(`${c.prefix}${e}-after-run`)){const d=t.getRawAttribute(`${c.prefix}${e}-after-run`);try{s.afterCallback=new Function("response",`
|
|
8
|
+
"use strict";
|
|
9
|
+
${d}
|
|
10
|
+
`)}catch(g){h.error("Haori",`Invalid after script: ${g}`)}}t.hasAttribute(f.attrName(e,"dialog"))&&(s.dialogMessage=t.getAttribute(f.attrName(e,"dialog"))),t.hasAttribute(f.attrName(e,"toast"))&&(s.toastMessage=t.getAttribute(f.attrName(e,"toast"))),t.hasAttribute(f.attrName(e,"redirect"))&&(s.redirectUrl=t.getAttribute(f.attrName(e,"redirect"))),["reset","refetch","click","open","close"].forEach(d=>{const g=f.attrName(e,d);if(!t.hasAttribute(g))return;const w=t.getRawAttribute(g),T=[];if(w?(document.body.querySelectorAll(w).forEach(x=>{const S=A.get(x);S&&T.push(S)}),T.length===0&&h.error("Haori",`Element not found: ${w} (${g})`)):T.push(t),T.length>0)switch(d){case"reset":s.resetFragments=T;break;case"refetch":s.refetchFragments=T;break;case"click":s.clickFragments=T;break;case"open":s.openFragments=T;break;case"close":s.closeFragments=T;break}})}if(!e){if(t.hasAttribute(f.attrName(null,"data",!0))){const o=t.getRawAttribute(f.attrName(null,"data",!0));s.data=N.parseDataBind(o)}if(t.hasAttribute(f.attrName(null,"form",!0))){const o=t.getRawAttribute(f.attrName(null,"form",!0));if(o){const d=document.body.querySelector(o);d!==null?s.formFragment=m.getFormFragment(A.get(d)):h.error("Haori",`Form element not found: ${o} (${f.attrName(null,"fetch-form",!0)})`)}else s.formFragment=m.getFormFragment(t)}}return r&&(!s.bindFragments||s.bindFragments.length===0)&&(s.bindFragments=[t]),s}static isElementFragment(t){if(typeof t!="object"||t===null)return!1;const e=t;return typeof e.getTarget=="function"&&typeof e.getChildElementFragments=="function"}constructor(t,e=null){f.isElementFragment(t)?this.options=f.buildOptions(t,e):this.options=t}run(){return Object.keys(this.options).length===0||this.options.formFragment&&this.validate(this.options.formFragment)===!1?Promise.resolve():this.confirm().then(t=>{if(!t)return Promise.resolve();let e=this.options.fetchUrl,s=this.options.fetchOptions;if(this.options.beforeCallback){const n=this.options.beforeCallback(e||null,s||null);if(n!=null){if(n===!1||typeof n=="object"&&n.stop)return Promise.resolve();typeof n=="object"&&(e="fetchUrl"in n?n.fetchUrl:e,s="fetchOptions"in n?n.fetchOptions:s)}}const i={};if(this.options.formFragment){const n=m.getValues(this.options.formFragment);Object.assign(i,n)}this.options.data&&typeof this.options.data=="object"&&Object.assign(i,this.options.data);const r=Object.keys(i).length>0;if(e){const n={...s||{}},a=new Headers(n.headers||void 0),u=(n.method||"GET").toUpperCase();if(u==="GET"||u==="HEAD"||u==="OPTIONS"){if(r){const b=new URL(e,window.location.href),l=new URLSearchParams(b.search);for(const[E,o]of Object.entries(i))o!==void 0&&(o===null?l.append(E,""):Array.isArray(o)?o.forEach(d=>{l.append(E,String(d))}):typeof o=="object"||typeof o=="function"?l.append(E,JSON.stringify(o)):l.append(E,String(o)));b.search=l.toString(),e=b.toString()}}else if(r){const b=a.get("Content-Type")||"";if(/multipart\/form-data/i.test(b)){a.delete("Content-Type");const l=new FormData;for(const[E,o]of Object.entries(i))o==null?l.append(E,""):o instanceof Blob?l.append(E,o):Array.isArray(o)?o.forEach(d=>l.append(E,String(d))):typeof o=="object"?l.append(E,JSON.stringify(o)):l.append(E,String(o));n.body=l}else if(/application\/x-www-form-urlencoded/i.test(b)){const l=new URLSearchParams;for(const[E,o]of Object.entries(i))o!==void 0&&(o===null?l.append(E,""):Array.isArray(o)?o.forEach(d=>l.append(E,String(d))):typeof o=="object"?l.append(E,JSON.stringify(o)):l.append(E,String(o)));n.body=l}else a.set("Content-Type","application/json"),n.body=JSON.stringify(i)}if(n.headers=a,this.options.targetFragment&&e){const b=performance.now();return y.fetchStart(this.options.targetFragment.getTarget(),e,n,r?i:void 0),fetch(e,n).then(l=>this.handleFetchResult(l,e||void 0,b)).catch(l=>{throw e&&y.fetchError(this.options.targetFragment.getTarget(),e,l),l})}else return e?fetch(e,n).then(b=>this.handleFetchResult(b,e||void 0)):Promise.resolve()}else{if((!this.options.bindFragments||this.options.bindFragments.length===0)&&this.options.formFragment&&r){const u=this.options.formFragment,b=u.getTarget();b.setAttribute(`${c.prefix}bind`,JSON.stringify(i));const l=u.getBindingData();return Object.assign(l,i),N.setBindingData(b,l)}const n=r?i:{},a=new Response(JSON.stringify(n),{headers:{"Content-Type":"application/json"}});return this.handleFetchResult(a)}})}handleFetchResult(t,e,s){if(!t.ok)return this.options.targetFragment&&e&&y.fetchError(this.options.targetFragment.getTarget(),e,new Error(`${t.status} ${t.statusText}`),t.status,s),this.handleFetchError(t);if(this.options.targetFragment&&e&&s&&y.fetchEnd(this.options.targetFragment.getTarget(),e,t.status,s),this.options.afterCallback){const r=this.options.afterCallback(t);if(r!=null){if(r===!1||typeof r=="object"&&r.stop)return Promise.resolve();typeof r=="object"&&"response"in r&&(t="response"in r?r.response:t)}}const i=[];return i.push(this.bindResult(t)),i.push(this.adjust()),i.push(this.addRow()),i.push(this.removeRow()),i.push(this.movePrevRow()),i.push(this.moveNextRow()),this.options.resetFragments&&this.options.resetFragments.length>0&&this.options.resetFragments.forEach(r=>{i.push(m.reset(r))}),this.options.refetchFragments&&this.options.refetchFragments.length>0&&this.options.refetchFragments.forEach(r=>{i.push(new f(r,null).run())}),this.options.clickFragments&&this.options.clickFragments.length>0&&this.options.clickFragments.forEach(r=>{const n=r.getTarget();typeof n.click=="function"?n.click():n.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0}))}),this.options.openFragments&&this.options.openFragments.length>0&&this.options.openFragments.forEach(r=>{const n=r.getTarget();n instanceof HTMLDialogElement?i.push(M.openDialog(n)):h.error("Haori","Element is not a dialog: ",n)}),this.options.closeFragments&&this.options.closeFragments.length>0&&this.options.closeFragments.forEach(r=>{const n=r.getTarget();n instanceof HTMLDialogElement?i.push(M.closeDialog(n)):h.error("Haori","Element is not a dialog: ",n)}),Promise.all(i).then(()=>this.options.dialogMessage?M.dialog(this.options.dialogMessage):Promise.resolve()).then(()=>this.options.toastMessage?M.toast(this.options.toastMessage,"info"):Promise.resolve()).then(()=>(this.options.redirectUrl&&(window.location.href=this.options.redirectUrl),Promise.resolve()))}async handleFetchError(t){let e=null;this.options.formFragment?e=this.options.formFragment:this.options.targetFragment&&(e=m.getFormFragment(this.options.targetFragment)||this.options.targetFragment);const s=async r=>{const n=e?e.getTarget():document.body;await M.addErrorMessage(n,r)};if((t.headers.get("Content-Type")||"").includes("application/json"))try{const r=await t.json(),n=[];if(r&&typeof r=="object"){if(typeof r.message=="string"&&n.push({message:r.message}),Array.isArray(r.messages))for(const a of r.messages)typeof a=="string"&&n.push({message:a});if(r.errors&&typeof r.errors=="object")for(const[a,u]of Object.entries(r.errors))Array.isArray(u)?n.push({key:a,message:u.join(`
|
|
11
|
+
`)}):typeof u=="string"?n.push({key:a,message:u}):u!=null&&n.push({key:a,message:String(u)});if(n.length===0)for(const[a,u]of Object.entries(r))a==="message"||a==="messages"||a==="errors"||(Array.isArray(u)?n.push({key:a,message:u.join(`
|
|
12
|
+
`)}):typeof u=="string"&&n.push({key:a,message:u}))}if(n.length===0){await s(`${t.status} ${t.statusText}`);return}for(const a of n)a.key&&e?await m.addErrorMessage(e,a.key,a.message):await s(a.message);return}catch{}try{const r=await t.text();r&&r.trim().length>0?await s(r.trim()):await s(`${t.status} ${t.statusText}`)}catch{await s(`${t.status} ${t.statusText}`)}}validate(t){if(this.options.valid!==!0)return!0;const e=t.getTarget();let s=this.validateOne(t);return s||e.focus(),t.getChildElementFragments().reverse().forEach(i=>{s&&=this.validate(i)}),s}validateOne(t){const e=t.getTarget();return e instanceof HTMLInputElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?e.reportValidity():!0}confirm(){const t=this.options.confirmMessage;return t==null?Promise.resolve(!0):M.confirm(t)}bindResult(t){return!this.options.bindFragments||this.options.bindFragments.length===0?Promise.resolve():(t.headers.get("Content-Type")?.includes("application/json")?t.json():t.text()).then(s=>{if(this.options.bindParams){const r={};this.options.bindParams.forEach(n=>{s&&typeof s=="object"&&n in s&&(r[n]=s[n])}),s=r}const i=[];if(this.options.bindArg)this.options.bindFragments.forEach(r=>{const n=r.getBindingData();n[this.options.bindArg]=s,i.push(N.setBindingData(r.getTarget(),n))});else{if(typeof s=="string")return h.error("Haori","string data cannot be bound without a bindArg."),Promise.reject(new Error("string data cannot be bound without a bindArg."));this.options.bindFragments.forEach(r=>{i.push(N.setBindingData(r.getTarget(),s))})}return Promise.all(i).then(()=>{})})}adjust(){if(!this.options.adjustFragments||this.options.adjustFragments.length===0)return Promise.resolve();const t=this.options.adjustValue??0,e=[];for(const s of this.options.adjustFragments){let i=s.getValue();(i==null||i==="")&&(i="0");let r=Number(i);isNaN(r)&&(r=0),r+=t,e.push(s.setValue(String(r)))}return Promise.all(e).then(()=>{})}getRowFragment(){if(!this.options.targetFragment)return h.error("Haori","Target fragment is not specified for row operation."),null;const t=this.options.targetFragment.closestByAttribute(`${c.prefix}row`);return t||(h.error("Haori","Row fragment not found."),null)}addRow(){if(this.options.rowAdd!==!0)return Promise.resolve();const t=this.getRowFragment();if(!t)return Promise.reject(new Error("Row fragment not found."));const e=[],s=t.clone();return e.push(t.getParent().insertAfter(s,t)),e.push(N.evaluateAll(s)),e.push(m.reset(s)),Promise.all(e).then(()=>{})}removeRow(){if(this.options.rowRemove!==!0)return Promise.resolve();const t=this.getRowFragment();if(!t)return Promise.reject(new Error("Row fragment not found."));const e=t.getParent();return e&&e.getChildElementFragments().filter(i=>!i.hasAttribute(`${c.prefix}each-before`)&&!i.hasAttribute(`${c.prefix}each-after`)).length<=1?Promise.resolve():t.remove()}movePrevRow(){if(this.options.rowMovePrev!==!0)return Promise.resolve();const t=this.getRowFragment();if(!t)return Promise.reject(new Error("Row fragment not found."));const e=t.getPrevious();if(!e)return Promise.resolve();const s=t.getParent();return s?s.insertBefore(t,e):Promise.resolve()}moveNextRow(){if(this.options.rowMoveNext!==!0)return Promise.resolve();const t=this.getRowFragment();if(!t)return Promise.reject(new Error("Row fragment not found."));const e=t.getNext();if(!e)return Promise.resolve();const s=t.getParent();return s?s.insertAfter(t,e):Promise.resolve()}}class J{static readParams(){const t={},e=window.location.search;return new URLSearchParams(e).forEach((i,r)=>{t[r]=i}),t}}class W{static async load(t,e){let s;try{s=await fetch(t,e)}catch(r){throw h.error("[Haori]","Failed to fetch import source:",t,r),new Error(`Failed to fetch: ${t}`)}if(!s.ok){const r=`${s.status} ${s.statusText}`;throw h.error("[Haori]","Import HTTP error:",t,r),new Error(`Failed to load ${t}: ${r}`)}let i;try{i=await s.text()}catch(r){throw h.error("[Haori]","Failed to read response text:",t,r),new Error(`Failed to read response from: ${t}`)}try{const n=new DOMParser().parseFromString(i,"text/html");return n&&n.body?n.body.innerHTML:(h.warn("[Haori]","No body found in imported document:",t),i)}catch(r){return h.error("[Haori]","Failed to parse imported HTML:",t,r),i}}}const p=class p{static isDeferredAttributeName(t){return p.DEFERRED_ATTRIBUTE_SUFFIXES.some(e=>t===`${c.prefix}${e}`)}static scan(t){const e=A.get(t);if(!e)return Promise.resolve();t.parentNode&&(A.get(t.parentNode)?.isMounted()||document.body.contains(t)?e.setMounted(!0):e.setMounted(!1));const s=[],i=new Set;for(const r of p.PRIORITY_ATTRIBUTE_SUFFIXES){const n=c.prefix+r;e.hasAttribute(n)&&(s.push(p.setAttribute(e.getTarget(),n,e.getRawAttribute(n))),i.add(n))}for(const r of e.getAttributeNames()){if(i.has(r)||p.isDeferredAttributeName(r))continue;const n=e.getRawAttribute(r);n!==null&&s.push(p.setAttribute(e.getTarget(),r,n))}for(const r of p.DEFERRED_ATTRIBUTE_SUFFIXES){const n=c.prefix+r;e.hasAttribute(n)&&(s.push(p.setAttribute(e.getTarget(),n,e.getRawAttribute(n))),i.add(n))}return e.getChildren().forEach(r=>{r instanceof F?s.push(p.scan(r.getTarget())):r instanceof j&&s.push(p.evaluateText(r))}),Promise.all(s).then(()=>{})}static setAttribute(t,e,s){const i=A.get(t),r=[];switch(e){case`${c.prefix}bind`:{s===null?(i.clearBindingDataCache(),i.setBindingData({})):i.setBindingData(p.parseDataBind(s));break}case`${c.prefix}if`:r.push(p.evaluateIf(i));break;case`${c.prefix}each`:r.push(p.evaluateEach(i));break;case`${c.prefix}fetch`:r.push(new f(i,null).run());break;case`${c.prefix}import`:{if(typeof s=="string"){const n=i.getTarget(),a=performance.now();y.importStart(n,s),r.push(W.load(s).then(u=>{const b=new TextEncoder().encode(u).length;return v.enqueue(()=>{n.innerHTML=u}).then(()=>{y.importEnd(n,s,b,a)})}).catch(u=>{y.importError(n,s,u),h.error("[Haori]","Failed to import HTML:",s,u)}))}break}case`${c.prefix}url-param`:{const n=i.getAttribute(`${c.prefix}url-arg`),a=J.readParams();if(n===null)p.setBindingData(t,a);else{const u=i.getRawBindingData()||{};u[String(n)]=a,p.setBindingData(t,u)}break}}return s===null?r.push(i.removeAttribute(e)):r.push(i.setAttribute(e,s)),Promise.all(r).then(()=>{})}static setBindingData(t,e){const s=A.get(t),i=s.getRawBindingData();s.setBindingData(e);const r=[];return r.push(s.setAttribute(`${c.prefix}bind`,JSON.stringify(e))),r.push(p.evaluateAll(s)),y.bindChange(t,i,e,"manual"),Promise.all(r).then(()=>{})}static parseDataBind(t){if(t.startsWith("{")||t.startsWith("["))try{return JSON.parse(t)}catch(e){return h.error("[Haori]","Invalid JSON in data-bind:",e),{}}else{const e=new URLSearchParams(t),s={};for(const[i,r]of e.entries())s[i]!==void 0?Array.isArray(s[i])?s[i].push(r):s[i]=[s[i],r]:s[i]=r;return s}}static addNode(t,e){const s=A.get(t);if(s.isSkipMutationNodes())return;const i=A.get(e.nextSibling),r=A.get(e);r&&(s.insertBefore(r,i),r instanceof F?p.scan(r.getTarget()):r instanceof j&&p.evaluateText(r))}static removeNode(t){const e=A.get(t);if(e){const s=e.getParent();if(s&&s.isSkipMutationNodes())return;e.remove()}}static changeText(t,e){const s=A.get(t);s&&s.setContent(e)}static changeValue(t,e){const s=A.get(t);if(s.getValue()===e)return Promise.resolve();const i=[];i.push(s.setValue(e));const r=p.getFormFragment(s);if(r){const n=m.getValues(r),a=r.getAttribute(`${c.prefix}form-arg`);let u;a?(u=r.getRawBindingData(),u||(u={}),u[String(a)]=n):u=n,i.push(p.setBindingData(r.getTarget(),u))}return Promise.all(i).then(()=>{})}static getFormFragment(t){if(t.getTarget()instanceof HTMLFormElement)return t;const e=t.getParent();return e?p.getFormFragment(e):null}static evaluateAll(t){const e=[];return t.hasAttribute(`${c.prefix}if`)&&e.push(p.evaluateIf(t)),t.hasAttribute(`${c.prefix}each`)&&e.push(p.evaluateEach(t)),t.getChildren().forEach(s=>{s instanceof F?e.push(p.evaluateAll(s)):s instanceof j&&e.push(p.evaluateText(s))}),Promise.all(e).then(()=>{})}static evaluateText(t){return t.evaluate()}static evaluateIf(t){const e=[],s=t.getAttribute(`${c.prefix}if`);return s===!1||s===void 0||s===null||Number.isNaN(s)?t.isVisible()&&e.push(t.hide().then(()=>{y.hide(t.getTarget())})):t.isVisible()||(e.push(t.show().then(()=>{y.show(t.getTarget())})),e.push(p.evaluateAll(t))),Promise.all(e).then(()=>{})}static evaluateEach(t){if(!t.isVisible()||!t.isMounted())return Promise.resolve();let e=t.getTemplate();if(e===null){const i=[];t.getChildren().forEach(n=>{if(n instanceof F){if(n.hasAttribute(`${c.prefix}each-before`)||n.hasAttribute(`${c.prefix}each-after`))return;if(e===null){e=n,t.removeChild(n);const a=n.getTarget();a.parentNode&&i.push(v.enqueue(()=>{a.parentNode&&a.parentNode.removeChild(a),n.setMounted(!1)}))}else h.warn("[Haori]","Template must be a single child element.")}}),t.setTemplate(e);const r=t.getAttribute(`${c.prefix}each`);return Array.isArray(r)?Promise.all(i).then(()=>this.updateDiff(t,r)):(h.error("[Haori]","Invalid each attribute:",r),Promise.reject(new Error("Invalid each attribute.")))}const s=t.getAttribute(`${c.prefix}each`);return Array.isArray(s)?this.updateDiff(t,s):(h.error("[Haori]","Invalid each attribute:",s),Promise.reject(new Error("Invalid each attribute.")))}static updateDiff(t,e){const s=t.getTemplate();if(s===null)return h.error("[Haori]","Template is not set for each element."),Promise.resolve();let i=t.getAttribute(`${c.prefix}each-index`);i&&(i=String(i));const r=t.getAttribute(`${c.prefix}each-key`),n=t.getAttribute(`${c.prefix}each-arg`),a=new Map,u=[];e.forEach((g,w)=>{const T=p.createListKey(g,r?String(r):null,w);u.push(T),a.set(T,{item:g,itemIndex:w})});const b=[];let l=t.getChildren().filter(g=>g instanceof F).filter(g=>!g.hasAttribute(`${c.prefix}each-before`)&&!g.hasAttribute(`${c.prefix}each-after`));l=l.filter(g=>u.indexOf(String(g.getListKey()))===-1?(b.push(g.remove()),!1):!0);const E=l.map(g=>g.getListKey()),o=t.getChildren().filter(g=>g instanceof F).filter(g=>g.hasAttribute(`${c.prefix}each-before`)).length;let d=Promise.resolve();return u.forEach((g,w)=>{const T=E.indexOf(g),{item:B,itemIndex:x}=a.get(g);let S;T!==-1?S=l[T]:S=s.clone(),p.updateRowFragment(S,B,i,x,n?String(n):null,g);const G=o+w;d=d.then(()=>t.insertBefore(S,t.getChildren()[G]||null).then(()=>p.evaluateAll(S)))}),Promise.all(b).then(()=>d).then(()=>{const g=u.filter(x=>x!==null),w=E.filter(x=>x!==null),T=g.filter(x=>!w.includes(x)),B=w.filter(x=>!g.includes(x));y.eachUpdate(t.getTarget(),T,B,g)})}static createListKey(t,e,s){let i;if(typeof t=="object"&&t!==null)if(e){const r=t[e];r==null?i=`__index_${s}`:typeof r=="object"?i=JSON.stringify(r):i=String(r)}else i=`__index_${s}`;else i=String(t);return i}static updateRowFragment(t,e,s,i,r,n){let a=e;if(typeof e=="object"&&e!==null)a={...e},s&&(a[s]=i),r&&(a={[r]:a});else if(r)a={[r]:e},s&&(a[s]=i);else{h.error("[Haori]",`Primitive value requires '${c.prefix}each-arg' attribute: ${e}`);return}t.setListKey(n),t.setAttribute(`${c.prefix}row`,n),t.setBindingData(a)}};p.PRIORITY_ATTRIBUTE_SUFFIXES=["bind","if","each"],p.DEFERRED_ATTRIBUTE_SUFFIXES=["fetch","url-param"];let N=p;class Y{constructor(t=document){this.onClick=e=>this.delegate(e,"click"),this.onChange=e=>this.delegate(e,"change"),this.onLoadCapture=e=>this.delegate(e,"load"),this.onWindowLoad=()=>{const e=document.documentElement,s=A.get(e);s&&new f(s,"load").run()},this.root=t}start(){this.root.addEventListener("click",this.onClick),this.root.addEventListener("change",this.onChange),this.root.addEventListener("load",this.onLoadCapture,!0),window.addEventListener("load",this.onWindowLoad,{once:!0})}stop(){this.root.removeEventListener("click",this.onClick),this.root.removeEventListener("change",this.onChange),this.root.removeEventListener("load",this.onLoadCapture,!0),window.removeEventListener("load",this.onWindowLoad)}delegate(t,e){const s=this.getElementFromTarget(t.target);if(!s)return;const i=A.get(s);i&&(e==="change"&&i instanceof F&&i.syncValue(),new f(i,e).run().catch(r=>{h.error("[Haori]","Procedure execution error:",r)}))}getElementFromTarget(t){return t?t instanceof HTMLElement?t:t instanceof Node?t.parentElement:null:null}}class O{static async init(){const t=await Promise.allSettled([N.scan(document.head),N.scan(document.body)]),[e,s]=t;e.status!=="fulfilled"&&h.error("[Haori]","Failed to build head fragment:",e.reason),s.status!=="fulfilled"&&h.error("[Haori]","Failed to build body fragment:",s.reason),O.observe(document.head),O.observe(document.body),new Y().start()}static observe(t){new MutationObserver(async s=>{for(const i of s)try{switch(i.type){case"attributes":{h.info("[Haori]","Attribute changed:",i.target,i.attributeName);const r=i.target;N.setAttribute(r,i.attributeName,r.getAttribute(i.attributeName));break}case"childList":{h.info("[Haori]","Child list changed:",Array.from(i.removedNodes).map(r=>r.nodeName),Array.from(i.addedNodes).map(r=>r.nodeName)),Array.from(i.removedNodes).forEach(r=>{N.removeNode(r)}),Array.from(i.addedNodes).forEach(r=>{r.parentElement instanceof HTMLElement&&N.addNode(r.parentElement,r)});break}case"characterData":{h.info("[Haori]","Character data changed:",i.target,i.target.textContent),i.target instanceof Text||i.target instanceof Comment?N.changeText(i.target,i.target.textContent):h.warn("[Haori]","Unsupported character data type:",i.target);break}default:h.warn("[Haori]","Unknown mutation type:",i.type);continue}}catch(r){h.error("[Haori]","Error processing mutation:",r)}}).observe(t,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),h.info("[Haori]","Observer initialized for",t)}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",O.init):O.init();const Q="0.1.0";exports.Core=N;exports.Env=c;exports.Form=m;exports.Fragment=A;exports.Haori=M;exports.Log=h;exports.Queue=v;exports.default=M;exports.version=Q;
|
|
13
|
+
//# sourceMappingURL=haori.cjs.js.map
|