forked from oguna/jsmigemo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
220 lines (189 loc) · 8.8 KB
/
index.html
File metadata and controls
220 lines (189 loc) · 8.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jsmigemo Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.page {
display: none;
}
.page:target {
display: block;
}
#statusBadge[data-kind="loading"] {
background: #fef3c7;
border-color: #f59e0b;
color: #78350f;
}
#statusBadge[data-kind="ready"] {
background: #d1fae5;
border-color: #10b981;
color: #065f46;
}
#statusBadge[data-kind="error"] {
background: #ffe4e6;
border-color: #f43f5e;
color: #881337;
}
</style>
</head>
<body class="bg-slate-50 text-slate-900">
<header class="border-b bg-white">
<div class="mx-auto max-w-5xl space-y-3 p-4">
<h1 class="text-xl font-bold">jsmigemo Demo</h1>
<input
id="queryInput"
type="text"
placeholder="kensaku / toukyou / neko"
class="w-full rounded-md border border-slate-300 px-3 py-2 outline-none focus:border-sky-500"
>
<nav class="flex flex-wrap items-center gap-2 text-sm">
<a href="#demo-regex" class="rounded border border-slate-300 bg-white px-3 py-1">Regex</a>
<a href="#demo-text" class="rounded border border-slate-300 bg-white px-3 py-1">Text Highlight</a>
<a href="#demo-list" class="rounded border border-slate-300 bg-white px-3 py-1">List Filter</a>
<span id="statusBadge" data-kind="loading" class="ml-auto rounded-full border px-3 py-1 text-xs font-semibold">辞書を読み込み中...</span>
</nav>
</div>
</header>
<main class="mx-auto max-w-5xl space-y-4 p-4">
<section id="demo-regex" class="page rounded border bg-white p-4">
<h2 class="mb-2 text-lg font-semibold">Generated Regex</h2>
<pre id="regexOutput" class="overflow-x-auto whitespace-pre-wrap rounded bg-slate-900 p-3 text-sm text-slate-100">検索語を入力してください。</pre>
</section>
<section id="demo-text" class="page rounded border bg-white p-4">
<h2 class="mb-2 text-lg font-semibold">Text Highlight</h2>
<pre id="wagahai" class="whitespace-pre-wrap rounded bg-slate-50 p-3 text-sm leading-7"></pre>
<p class="mt-2 text-xs text-slate-500">夏目漱石『吾輩は猫である』</p>
</section>
<section id="demo-list" class="page rounded border bg-white p-4">
<h2 class="mb-2 text-lg font-semibold">List Filter</h2>
<p id="listCount" class="mb-2 text-sm text-slate-700"></p>
<ul id="prefectureList" class="grid gap-2 sm:grid-cols-2 lg:grid-cols-3"></ul>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/jsmigemo/dist/jsmigemo.min.iife.js"></script>
<script>
const DICT_URL = "https://cdn.jsdelivr.net/npm/jsmigemo/migemo-compact-dict";
const VALID_HASHES = new Set(["#demo-regex", "#demo-text", "#demo-list"]);
const WAGAHAI_TEXT =
"吾輩は猫である。名前はまだ無い。\n" +
"どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕まえて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始めであろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙を吹く。どうも咽せぽくて実に弱った。これが人間の飲む煙草というものである事はようやくこの頃知った。";
const PREFECTURES = [
"北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県",
"埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県",
"岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県",
"鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県",
"佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"
];
const queryInput = document.getElementById("queryInput");
const regexOutput = document.getElementById("regexOutput");
const wagahai = document.getElementById("wagahai");
const listCount = document.getElementById("listCount");
const prefectureList = document.getElementById("prefectureList");
const statusBadge = document.getElementById("statusBadge");
let migemo = null;
const escapeMap = {
"&": "&",
"<": "<",
">": ">",
"\"": """,
"'": "'"
};
const escapeHtml = (text) => text.replace(/[&<>"']/g, (ch) => escapeMap[ch]);
function ensureHash() {
if (!VALID_HASHES.has(window.location.hash)) {
window.location.hash = "demo-regex";
}
}
function setStatus(text, kind) {
statusBadge.textContent = text;
statusBadge.dataset.kind = kind;
}
function highlight(text, regex) {
regex.lastIndex = 0;
let start = 0;
let result = "";
let match;
while ((match = regex.exec(text)) !== null) {
if (match[0].length === 0) {
regex.lastIndex += 1;
continue;
}
result += escapeHtml(text.slice(start, match.index));
result += `<mark class="rounded bg-rose-300 px-1">${escapeHtml(match[0])}</mark>`;
start = match.index + match[0].length;
}
result += escapeHtml(text.slice(start));
return result;
}
function renderList(values, regex = null) {
if (!regex) {
prefectureList.innerHTML = values
.map((name) => `<li class="rounded border bg-white px-3 py-2 text-sm">${escapeHtml(name)}</li>`)
.join("");
return;
}
prefectureList.innerHTML = values
.map((name) => `<li class="rounded border bg-white px-3 py-2 text-sm">${highlight(name, regex)}</li>`)
.join("");
}
function resetView() {
regexOutput.textContent = "検索語を入力してください。";
wagahai.textContent = WAGAHAI_TEXT;
renderList(PREFECTURES);
listCount.textContent = `${PREFECTURES.length}件 / 全${PREFECTURES.length}件`;
}
function updateView() {
const query = queryInput.value.trim();
if (!query || !migemo) {
resetView();
return;
}
const source = migemo.query(query);
regexOutput.textContent = source;
let highlightRegex;
let filterRegex;
try {
highlightRegex = new RegExp(source, "giu");
filterRegex = new RegExp(source, "iu");
} catch (error) {
regexOutput.textContent = `正規表現の生成に失敗: ${error.message}`;
resetView();
return;
}
wagahai.innerHTML = highlight(WAGAHAI_TEXT, highlightRegex);
const filtered = PREFECTURES.filter((name) => filterRegex.test(name));
renderList(filtered, highlightRegex);
listCount.textContent = `${filtered.length}件 / 全${PREFECTURES.length}件`;
}
async function loadDictionary() {
setStatus("辞書を読み込み中...", "loading");
try {
if (!window.jsmigemo) {
throw new Error("jsmigemo script was not loaded");
}
const response = await fetch(DICT_URL);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const buffer = await response.arrayBuffer();
const dict = new window.jsmigemo.CompactDictionary(buffer);
migemo = new window.jsmigemo.Migemo();
migemo.setDict(dict);
setStatus("辞書読み込み完了", "ready");
updateView();
} catch (error) {
setStatus("辞書読み込み失敗", "error");
regexOutput.textContent = `辞書の読み込みに失敗: ${error.message}`;
}
}
queryInput.addEventListener("input", updateView);
window.addEventListener("hashchange", ensureHash);
ensureHash();
resetView();
loadDictionary();
</script>
</body>
</html>