@basic-genomics/hivtrace-viz 1.0.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/README.md +161 -0
- package/dist/060b2710bdbbe3dfe48b.svg +288 -0
- package/dist/1815e00441357e01619e.ttf +0 -0
- package/dist/2463b90d9a316e4e5294.woff2 +0 -0
- package/dist/2582b0e4bcf85eceead0.ttf +0 -0
- package/dist/4692b9ec53fd5972caa2.ttf +0 -0
- package/dist/5be1347c682810f199c7.eot +0 -0
- package/dist/82b1212e45a2bc35dd73.woff +0 -0
- package/dist/89999bdf5d835c012025.woff2 +0 -0
- package/dist/914997e1bdfc990d0897.ttf +0 -0
- package/dist/be810be3a3e14c682a25.woff2 +0 -0
- package/dist/c210719e60948b211a12.woff2 +0 -0
- package/dist/da94ef451f4969af06e6.ttf +0 -0
- package/dist/ea8f94e1d22e0d35ccd4.woff2 +0 -0
- package/dist/embed/060b2710bdbbe3dfe48b.svg +288 -0
- package/dist/embed/1815e00441357e01619e.ttf +0 -0
- package/dist/embed/2463b90d9a316e4e5294.woff2 +0 -0
- package/dist/embed/2582b0e4bcf85eceead0.ttf +0 -0
- package/dist/embed/4692b9ec53fd5972caa2.ttf +0 -0
- package/dist/embed/5be1347c682810f199c7.eot +0 -0
- package/dist/embed/82b1212e45a2bc35dd73.woff +0 -0
- package/dist/embed/89999bdf5d835c012025.woff2 +0 -0
- package/dist/embed/914997e1bdfc990d0897.ttf +0 -0
- package/dist/embed/be810be3a3e14c682a25.woff2 +0 -0
- package/dist/embed/c210719e60948b211a12.woff2 +0 -0
- package/dist/embed/da94ef451f4969af06e6.ttf +0 -0
- package/dist/embed/ea8f94e1d22e0d35ccd4.woff2 +0 -0
- package/dist/embed/hivtrace.css +19152 -0
- package/dist/embed/hivtrace.css.map +1 -0
- package/dist/embed/hivtrace.js +3 -0
- package/dist/embed/hivtrace.js.LICENSE.txt +38 -0
- package/dist/embed/hivtrace.js.map +1 -0
- package/dist/embed/index.html +1318 -0
- package/dist/embed/locales/en-US.json +168 -0
- package/dist/embed/locales/zh-CN.json +168 -0
- package/dist/hivtrace.css +19152 -0
- package/dist/hivtrace.css.map +1 -0
- package/dist/hivtrace.js +3 -0
- package/dist/hivtrace.js.LICENSE.txt +38 -0
- package/dist/hivtrace.js.map +1 -0
- package/dist/react/HivtraceViz.d.ts +8 -0
- package/dist/react/HivtraceViz.d.ts.map +1 -0
- package/dist/react/HivtraceViz.js +169 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +1 -0
- package/dist/react/types.d.ts +82 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +5 -0
- package/dist/vite-plugin/index.d.ts +13 -0
- package/dist/vite-plugin/index.d.ts.map +1 -0
- package/dist/vite-plugin/index.js +83 -0
- package/package.json +106 -0
|
@@ -0,0 +1,1318 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
|
+
<title>HIV-TRACE</title>
|
|
9
|
+
|
|
10
|
+
<!-- 样式 -->
|
|
11
|
+
<link href="./hivtrace.css" rel="stylesheet" />
|
|
12
|
+
<link rel="shortcut icon" href="#" />
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
body {
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* 搜索框固定宽度 */
|
|
21
|
+
#filter_input {
|
|
22
|
+
width: 250px !important;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.row,
|
|
26
|
+
.col-lg-12,
|
|
27
|
+
.col-lg-6,
|
|
28
|
+
.col-lg-4,
|
|
29
|
+
.col-lg-3,
|
|
30
|
+
.col-lg-2,
|
|
31
|
+
.col-lg-1,
|
|
32
|
+
.col-lg-11,
|
|
33
|
+
.col-lg-10,
|
|
34
|
+
.col-lg-9,
|
|
35
|
+
.col-lg-8,
|
|
36
|
+
.col-lg-7,
|
|
37
|
+
.col-lg-5 {
|
|
38
|
+
margin: 0 !important;
|
|
39
|
+
padding: 0 1px !important;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* 全屏加载态样式 - 中性简洁设计 */
|
|
43
|
+
.hivtrace-loading-overlay {
|
|
44
|
+
position: fixed;
|
|
45
|
+
top: 0;
|
|
46
|
+
left: 0;
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
background: rgba(255, 255, 255, 0.95);
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
z-index: 9999;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.hivtrace-loading-overlay.hidden {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.hivtrace-loading-spinner {
|
|
62
|
+
width: 40px;
|
|
63
|
+
height: 40px;
|
|
64
|
+
border: 3px solid #e0e0e0;
|
|
65
|
+
border-top-color: #666;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
animation: hivtrace-spin 0.8s linear infinite;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@keyframes hivtrace-spin {
|
|
71
|
+
to {
|
|
72
|
+
transform: rotate(360deg);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.hivtrace-loading-text {
|
|
77
|
+
color: #666;
|
|
78
|
+
font-size: 14px;
|
|
79
|
+
margin-top: 16px;
|
|
80
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* 错误态样式 */
|
|
84
|
+
.hivtrace-loading-overlay.error {
|
|
85
|
+
background: rgba(255, 255, 255, 0.98);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.hivtrace-error-icon {
|
|
89
|
+
font-size: 48px;
|
|
90
|
+
color: #dc3545;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.hivtrace-error-message {
|
|
94
|
+
color: #666;
|
|
95
|
+
font-size: 14px;
|
|
96
|
+
margin-top: 12px;
|
|
97
|
+
text-align: center;
|
|
98
|
+
max-width: 300px;
|
|
99
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.hivtrace-retry-btn {
|
|
103
|
+
margin-top: 16px;
|
|
104
|
+
padding: 8px 20px;
|
|
105
|
+
background: #f5f5f5;
|
|
106
|
+
color: #333;
|
|
107
|
+
border: 1px solid #ddd;
|
|
108
|
+
border-radius: 4px;
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
transition: background 0.2s;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.hivtrace-retry-btn:hover {
|
|
115
|
+
background: #e8e8e8;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* 状态栏样式 - 简洁中性 */
|
|
119
|
+
.hivtrace-status-bar {
|
|
120
|
+
width: 100%;
|
|
121
|
+
padding: 4px 8px;
|
|
122
|
+
margin: 0;
|
|
123
|
+
display: flex;
|
|
124
|
+
justify-content: flex-end;
|
|
125
|
+
align-items: center;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.hivtrace-status-bar #network_status_string {
|
|
129
|
+
flex-shrink: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* 全屏按钮 - 仿宿主应用 outline 风格 */
|
|
133
|
+
.hivtrace-fullscreen-btn {
|
|
134
|
+
margin-left: 4px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.hivtrace-fullscreen-btn button {
|
|
138
|
+
display: inline-flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
padding: 6px 8px;
|
|
142
|
+
border: 1px solid #e2e8f0;
|
|
143
|
+
border-radius: 6px;
|
|
144
|
+
background: #fff;
|
|
145
|
+
color: #374151;
|
|
146
|
+
font-size: 14px;
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
transition: background 0.15s, color 0.15s;
|
|
149
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.hivtrace-fullscreen-btn button:hover {
|
|
153
|
+
background: #f4f4f5;
|
|
154
|
+
color: #18181b;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* 状态栏 badge 和 label 样式优化 */
|
|
158
|
+
.hivtrace-status-bar .badge {
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
min-width: 32px;
|
|
163
|
+
padding: 4px 10px;
|
|
164
|
+
font-size: 13px;
|
|
165
|
+
font-weight: 500;
|
|
166
|
+
border-radius: 12px;
|
|
167
|
+
background: #374151;
|
|
168
|
+
color: #fff;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.hivtrace-status-bar .label {
|
|
172
|
+
display: inline-flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
padding: 3px 8px;
|
|
175
|
+
font-size: 12px;
|
|
176
|
+
font-weight: 400;
|
|
177
|
+
border-radius: 6px;
|
|
178
|
+
margin-right: 12px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.hivtrace-status-bar .label-primary {
|
|
182
|
+
background: #dbeafe;
|
|
183
|
+
color: #1e40af;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* 表格内操作按钮样式 */
|
|
187
|
+
.table .btn-group {
|
|
188
|
+
display: inline-flex;
|
|
189
|
+
gap: 4px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.table .btn-xs,
|
|
193
|
+
.table .btn-sm {
|
|
194
|
+
background: #3b82f6;
|
|
195
|
+
border-color: #3b82f6;
|
|
196
|
+
color: #fff;
|
|
197
|
+
border-radius: 4px;
|
|
198
|
+
font-size: 12px;
|
|
199
|
+
padding: 2px 8px;
|
|
200
|
+
margin-right: 4px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.table .btn-xs:hover,
|
|
204
|
+
.table .btn-sm:hover {
|
|
205
|
+
background: #2563eb;
|
|
206
|
+
border-color: #2563eb;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.table .btn-xs:last-child,
|
|
210
|
+
.table .btn-sm:last-child {
|
|
211
|
+
margin-right: 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* 网络标签页操作栏 - 超出换行 */
|
|
215
|
+
#network_ui_bar {
|
|
216
|
+
display: flex;
|
|
217
|
+
flex-wrap: wrap;
|
|
218
|
+
align-items: center;
|
|
219
|
+
gap: 4px 8px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
#network_ui_bar>.input-group-btn,
|
|
223
|
+
#network_ui_bar>.btn,
|
|
224
|
+
#network_ui_bar>.input-group {
|
|
225
|
+
flex-shrink: 0;
|
|
226
|
+
width: auto;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
/* 图例区域样式 - 限制最大高度和滚动 */
|
|
231
|
+
#legend_holder {
|
|
232
|
+
max-height: 300px;
|
|
233
|
+
overflow-y: auto;
|
|
234
|
+
overflow-x: hidden;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.hiv-trace-legend text {
|
|
238
|
+
word-wrap: break-word;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* 动态标签页关闭按钮样式 */
|
|
242
|
+
.nav-tabs>li>a .close {
|
|
243
|
+
margin-left: 8px;
|
|
244
|
+
font-size: 16px;
|
|
245
|
+
line-height: 1;
|
|
246
|
+
opacity: 0.6;
|
|
247
|
+
vertical-align: middle;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.nav-tabs>li>a .close:hover {
|
|
251
|
+
opacity: 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* 下拉菜单样式优化 */
|
|
255
|
+
.dropdown-menu {
|
|
256
|
+
padding: 8px 0;
|
|
257
|
+
min-width: 200px;
|
|
258
|
+
max-height: 400px;
|
|
259
|
+
overflow-y: auto;
|
|
260
|
+
border-radius: 8px;
|
|
261
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.dropdown-menu .form-group {
|
|
265
|
+
padding: 8px 16px;
|
|
266
|
+
margin: 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.dropdown-menu .form-group label {
|
|
270
|
+
margin-bottom: 6px;
|
|
271
|
+
font-weight: 500;
|
|
272
|
+
color: #374151;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.dropdown-menu .form-group .form-control {
|
|
276
|
+
border-radius: 6px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.dropdown-menu>li>a {
|
|
280
|
+
padding: 8px 16px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* ========================================
|
|
284
|
+
属性标签页 - 新布局
|
|
285
|
+
顶部横向 pills + 下方左图右表
|
|
286
|
+
======================================== */
|
|
287
|
+
|
|
288
|
+
.hivtrace-attributes-container {
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
height: 100%;
|
|
292
|
+
min-height: 500px;
|
|
293
|
+
padding: 12px;
|
|
294
|
+
gap: 12px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* 顶部:横向 pills 选择器 */
|
|
298
|
+
.hivtrace-attributes-header {
|
|
299
|
+
flex-shrink: 0;
|
|
300
|
+
background: #f9fafb;
|
|
301
|
+
border: 1px solid #e5e7eb;
|
|
302
|
+
border-radius: 8px;
|
|
303
|
+
padding: 12px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.hivtrace-pill-row {
|
|
307
|
+
display: flex;
|
|
308
|
+
flex-wrap: wrap;
|
|
309
|
+
gap: 8px;
|
|
310
|
+
max-height: 120px;
|
|
311
|
+
overflow-y: auto;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* 横向 Pill 样式 */
|
|
315
|
+
.hivtrace-pill {
|
|
316
|
+
display: inline-flex;
|
|
317
|
+
align-items: center;
|
|
318
|
+
padding: 6px 12px;
|
|
319
|
+
font-size: 13px;
|
|
320
|
+
font-weight: 400;
|
|
321
|
+
color: #374151;
|
|
322
|
+
background: #fff;
|
|
323
|
+
border: 1px solid #e5e7eb;
|
|
324
|
+
border-radius: 16px;
|
|
325
|
+
cursor: pointer;
|
|
326
|
+
transition: all 0.15s ease;
|
|
327
|
+
white-space: nowrap;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.hivtrace-pill:hover {
|
|
331
|
+
background: #f3f4f6;
|
|
332
|
+
border-color: #d1d5db;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.hivtrace-pill.active {
|
|
336
|
+
background: #dbeafe;
|
|
337
|
+
border-color: #3b82f6;
|
|
338
|
+
color: #1e40af;
|
|
339
|
+
font-weight: 500;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.hivtrace-pill .badge {
|
|
343
|
+
margin-left: 6px;
|
|
344
|
+
padding: 2px 6px;
|
|
345
|
+
font-size: 11px;
|
|
346
|
+
font-weight: 500;
|
|
347
|
+
background: #e5e7eb;
|
|
348
|
+
color: #6b7280;
|
|
349
|
+
border-radius: 10px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.hivtrace-pill.active .badge {
|
|
353
|
+
background: #bfdbfe;
|
|
354
|
+
color: #1e40af;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* 下方:图表和表格上下排列 */
|
|
358
|
+
.hivtrace-attributes-content {
|
|
359
|
+
flex: 1;
|
|
360
|
+
display: flex;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
gap: 12px;
|
|
363
|
+
min-height: 0;
|
|
364
|
+
overflow: auto;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* 可视化面板 */
|
|
368
|
+
.hivtrace-viz-panel {
|
|
369
|
+
flex-shrink: 0;
|
|
370
|
+
display: flex;
|
|
371
|
+
flex-direction: column;
|
|
372
|
+
background: #f9fafb;
|
|
373
|
+
border: 1px solid #e5e7eb;
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
padding: 12px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.hivtrace-panel-toolbar {
|
|
379
|
+
display: flex;
|
|
380
|
+
align-items: center;
|
|
381
|
+
justify-content: flex-end;
|
|
382
|
+
gap: 12px;
|
|
383
|
+
margin-bottom: 8px;
|
|
384
|
+
flex-shrink: 0;
|
|
385
|
+
min-height: 32px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.hivtrace-panel-toolbar .btn {
|
|
389
|
+
display: inline-flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
justify-content: center;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.hivtrace-viz-wrapper {
|
|
395
|
+
display: flex;
|
|
396
|
+
align-items: center;
|
|
397
|
+
justify-content: center;
|
|
398
|
+
min-height: 250px;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* 表格面板 */
|
|
402
|
+
.hivtrace-table-panel {
|
|
403
|
+
flex: 1;
|
|
404
|
+
display: flex;
|
|
405
|
+
flex-direction: column;
|
|
406
|
+
background: #f9fafb;
|
|
407
|
+
border: 1px solid #e5e7eb;
|
|
408
|
+
border-radius: 8px;
|
|
409
|
+
padding: 12px;
|
|
410
|
+
min-height: 200px;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.hivtrace-table-wrapper {
|
|
414
|
+
flex: 1;
|
|
415
|
+
overflow: auto;
|
|
416
|
+
background: #fff;
|
|
417
|
+
border: 1px solid #e5e7eb;
|
|
418
|
+
border-radius: 6px;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.hivtrace-table-wrapper table {
|
|
422
|
+
margin: 0;
|
|
423
|
+
width: 100%;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.hivtrace-table-content table {
|
|
427
|
+
margin: 0;
|
|
428
|
+
width: 100%;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.hivtrace-toggle-label {
|
|
432
|
+
display: inline-flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
gap: 6px;
|
|
435
|
+
font-size: 13px;
|
|
436
|
+
color: #6b7280;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.hivtrace-toggle-label input[type="checkbox"] {
|
|
441
|
+
margin: 0;
|
|
442
|
+
vertical-align: middle;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* 空状态 */
|
|
446
|
+
.hivtrace-empty-state {
|
|
447
|
+
position: absolute;
|
|
448
|
+
top: 50%;
|
|
449
|
+
left: 50%;
|
|
450
|
+
transform: translate(-50%, -50%);
|
|
451
|
+
text-align: center;
|
|
452
|
+
color: #9ca3af;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.hivtrace-empty-state i {
|
|
456
|
+
font-size: 48px;
|
|
457
|
+
margin-bottom: 16px;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.hivtrace-empty-state p {
|
|
461
|
+
font-size: 14px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* 主内容区默认不可见但保留布局 */
|
|
465
|
+
.hivtrace-main-content {
|
|
466
|
+
visibility: hidden;
|
|
467
|
+
opacity: 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.hivtrace-main-content.loaded {
|
|
471
|
+
visibility: visible;
|
|
472
|
+
opacity: 1;
|
|
473
|
+
}
|
|
474
|
+
</style>
|
|
475
|
+
</head>
|
|
476
|
+
|
|
477
|
+
<body>
|
|
478
|
+
<!-- 全屏加载态覆盖层 -->
|
|
479
|
+
<div id="hivtrace-loading" class="hivtrace-loading-overlay">
|
|
480
|
+
<div class="hivtrace-loading-spinner"></div>
|
|
481
|
+
<div class="hivtrace-loading-text">加载网络数据中...</div>
|
|
482
|
+
</div>
|
|
483
|
+
|
|
484
|
+
<!-- 主内容区(默认隐藏,加载完成后显示) -->
|
|
485
|
+
<div id="hivtrace-main" class="hivtrace-main-content">
|
|
486
|
+
<!-- 状态栏(右对齐) -->
|
|
487
|
+
<div class="hivtrace-status-bar">
|
|
488
|
+
<div style="flex: 1;"></div>
|
|
489
|
+
<div id="network_status_string"></div>
|
|
490
|
+
<span id="fullscreen_toggle_span" class="hivtrace-fullscreen-btn">
|
|
491
|
+
<button type="button" class="btn btn-link btn-sm" id="fullscreen_toggle_btn" title="进入全屏">
|
|
492
|
+
<i class="fa fa-expand" id="fullscreen_icon"></i>
|
|
493
|
+
</button>
|
|
494
|
+
</span>
|
|
495
|
+
</div>
|
|
496
|
+
|
|
497
|
+
<div style="width: 100%; max-width: none; padding: 0; margin: 0;">
|
|
498
|
+
|
|
499
|
+
<div class="modal fade" data-hivtrace-ui-role="cluster_list" tabindex="-1" role="dialog">
|
|
500
|
+
<div class="modal-dialog" role="document">
|
|
501
|
+
<div class="modal-content" data-hivtrace-ui-role="cluster_list_body">
|
|
502
|
+
<div class="modal-header">
|
|
503
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
504
|
+
<span aria-hidden="true">×</span>
|
|
505
|
+
</button>
|
|
506
|
+
<h4 class="modal-title">列出簇中的节点</h4>
|
|
507
|
+
<button type="button" data-hivtrace-ui-role="cluster_list_view_toggle" class="btn btn-primary pull-right"
|
|
508
|
+
data-view="attribute">
|
|
509
|
+
按 ID 分组
|
|
510
|
+
</button>
|
|
511
|
+
<button type="button" class="btn btn-default btn-small">
|
|
512
|
+
<i class="fa fa-download" data-hivtrace-ui-role="cluster_list_data_export"></i>
|
|
513
|
+
</button>
|
|
514
|
+
</div>
|
|
515
|
+
<ul data-hivtrace-ui-role="cluster_list_payload" class="list-unstyled"></ul>
|
|
516
|
+
<div class="modal-footer">
|
|
517
|
+
<!--<button type="button" class="btn btn-primary" id = "network_ui_bar_cluster_zoom_svg_export">Export <i class = "fa fa-image"></i></button>-->
|
|
518
|
+
<button type="button" class="btn btn-default" data-dismiss="modal">
|
|
519
|
+
关闭
|
|
520
|
+
</button>
|
|
521
|
+
</div>
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
|
|
526
|
+
<div class="tabbable">
|
|
527
|
+
<ul class="nav nav-tabs main-hivtrace-tabs" id="top_level_tab_container">
|
|
528
|
+
<li class="active" id="main-tab">
|
|
529
|
+
<a id="trace-default-tab" href="#trace-results" data-toggle="tab">网络</a>
|
|
530
|
+
</li>
|
|
531
|
+
<li class="disabled" id="graph-tab">
|
|
532
|
+
<a href="#trace-graph" data-toggle="tab">统计</a>
|
|
533
|
+
</li>
|
|
534
|
+
<li class="disabled" id="clusters-tab">
|
|
535
|
+
<a href="#trace-clusters" data-toggle="tab">簇</a>
|
|
536
|
+
</li>
|
|
537
|
+
<li class="disabled" id="nodes-tab">
|
|
538
|
+
<a href="#trace-nodes" data-toggle="tab">节点</a>
|
|
539
|
+
</li>
|
|
540
|
+
<li class="disabled" id="attributes-tab">
|
|
541
|
+
<a href="#trace-attributes" data-toggle="tab">属性</a>
|
|
542
|
+
</li>
|
|
543
|
+
</ul>
|
|
544
|
+
|
|
545
|
+
<div class="tab-content" id="top_level_tab_content">
|
|
546
|
+
<div id="trace-results" class="tab-pane active">
|
|
547
|
+
<!-- Warning bar -->
|
|
548
|
+
<div class="row" style="margin-top: 10px; margin-bottom: 10 px">
|
|
549
|
+
<div class="col-lg-12">
|
|
550
|
+
<div class="alert alert-warning alert-dismissible" id="main-warning" style="display: none">
|
|
551
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
552
|
+
<span aria-hidden="true">×</span>
|
|
553
|
+
</button>
|
|
554
|
+
</div>
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
|
|
558
|
+
<div class="row" data-hivtrace="cluster-clone">
|
|
559
|
+
<div class="col-lg-12">
|
|
560
|
+
<div class="nav-trace">
|
|
561
|
+
<div class="input-group input-group-sm">
|
|
562
|
+
<!-- UI Bar -->
|
|
563
|
+
<div class="nav-trace">
|
|
564
|
+
<div class="input-group input-group-sm" id="network_ui_bar" data-hivtrace-button-bar="yes">
|
|
565
|
+
<div class="input-group-btn" data-hivtrace-ui-role="export_image"></div>
|
|
566
|
+
<div class="input-group-btn" data-hivtrace-ui-role="button_group"></div>
|
|
567
|
+
<span class="input-group-btn">
|
|
568
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
|
569
|
+
data-hivtrace-ui-role="cluster_operations_button">
|
|
570
|
+
簇 <span class="caret"></span>
|
|
571
|
+
</button>
|
|
572
|
+
<ul class="dropdown-menu" role="menu" data-hivtrace-ui-role="cluster_operations_container">
|
|
573
|
+
</ul>
|
|
574
|
+
</span>
|
|
575
|
+
|
|
576
|
+
<!-- Dropdowns to Format Notes By Attributes (Color, Shape, Opacity and Label) -->
|
|
577
|
+
<div class="input-group-btn">
|
|
578
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
|
579
|
+
data-hivtrace-ui-role="attributes_label">
|
|
580
|
+
颜色 <span class="caret"></span>
|
|
581
|
+
</button>
|
|
582
|
+
<ul class="dropdown-menu" role="menu" data-hivtrace-ui-role="attributes"></ul>
|
|
583
|
+
<button type="button" class="btn btn-default" data-toggle="collapse" href="#colorPickerRow"
|
|
584
|
+
title="Select Color Palette">
|
|
585
|
+
<i class="fa fa-circle color-picker-toggle" aria-hidden="true"></i>
|
|
586
|
+
</button>
|
|
587
|
+
<button type="button" class="btn btn-default" style="display: none"
|
|
588
|
+
data-hivtrace-ui-role="attributes_invert">
|
|
589
|
+
反转
|
|
590
|
+
</button>
|
|
591
|
+
</div>
|
|
592
|
+
<div class="input-group-btn">
|
|
593
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
|
594
|
+
data-hivtrace-ui-role="shapes_label">
|
|
595
|
+
形状 <span class="caret"></span>
|
|
596
|
+
</button>
|
|
597
|
+
<ul class="dropdown-menu" role="menu" data-hivtrace-ui-role="shapes"></ul>
|
|
598
|
+
</div>
|
|
599
|
+
<div class="input-group-btn">
|
|
600
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
|
601
|
+
data-hivtrace-ui-role="opacity_label">
|
|
602
|
+
透明度 <span class="caret"></span>
|
|
603
|
+
</button>
|
|
604
|
+
<ul class="dropdown-menu" role="menu" data-hivtrace-ui-role="opacity"></ul>
|
|
605
|
+
<button type="button" class="btn btn-default" style="display: none"
|
|
606
|
+
data-hivtrace-ui-role="opacity_invert">
|
|
607
|
+
反转
|
|
608
|
+
</button>
|
|
609
|
+
</div>
|
|
610
|
+
|
|
611
|
+
<div class="input-group-btn" style="display: none"
|
|
612
|
+
data-hivtrace-ui-role="extra_operations_enclosure">
|
|
613
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
|
614
|
+
data-hivtrace-ui-role="extra_label">
|
|
615
|
+
操作 <span class="caret"></span>
|
|
616
|
+
</button>
|
|
617
|
+
<ul class="dropdown-menu" role="menu" data-hivtrace-ui-role="extra_operations_container"></ul>
|
|
618
|
+
</div>
|
|
619
|
+
|
|
620
|
+
<div class="input-group input-group-sm">
|
|
621
|
+
<span class="input-group-addon">
|
|
622
|
+
<i class="fa fa-search"></i>
|
|
623
|
+
</span>
|
|
624
|
+
<input id="filter_input" type="text" class="form-control" placeholder="在属性中搜索文本"
|
|
625
|
+
data-hivtrace-ui-role="filter" />
|
|
626
|
+
<span class="input-group-addon">
|
|
627
|
+
隐藏其他
|
|
628
|
+
<input type="checkbox" data-hivtrace-ui-role="hide_filter" />
|
|
629
|
+
</span>
|
|
630
|
+
<span class="input-group-addon set-min-cluster-size">
|
|
631
|
+
最小簇大小
|
|
632
|
+
<input id="min_cluster_size_input" type="number"
|
|
633
|
+
data-hivtrace-ui-role="set_min_cluster_size" placeholder="2" min="0" max="9999" />
|
|
634
|
+
</span>
|
|
635
|
+
<span class="input-group-addon" id="network_ui_bar_legend_icon_span">
|
|
636
|
+
<a data-toggle="collapse" href="#network_ui_bar_legend" aria-expanded="false"
|
|
637
|
+
aria-controls="network_ui_bar_legend">
|
|
638
|
+
<i class="fa fa-question"></i>
|
|
639
|
+
</a>
|
|
640
|
+
</span>
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
646
|
+
|
|
647
|
+
<!-- Legend SVG -->
|
|
648
|
+
<div class="row collapse" id="network_ui_bar_legend" style="margin-top: 0.1em"
|
|
649
|
+
data-hivtrace="cluster-clone">
|
|
650
|
+
<div class="well">
|
|
651
|
+
<span class="label label-primary">簇</span> 显示为
|
|
652
|
+
带粗边框的圆形
|
|
653
|
+
<svg width="20" height="20" style="vertical-align: bottom; display: inline">
|
|
654
|
+
<circle cx="10" cy="10" r="7" class="cluster"></circle>
|
|
655
|
+
</svg>
|
|
656
|
+
,<em>面积</em>与簇中节点数量成正比。点击簇可显示菜单选项,拖动可重新定位。
|
|
657
|
+
<p></p>
|
|
658
|
+
<span class="label label-primary">节点</span> 显示为
|
|
659
|
+
不同的符号(取决于渲染选项),带细边框
|
|
660
|
+
<svg width="20" height="20" style="vertical-align: bottom; display: inline">
|
|
661
|
+
<circle cx="10" cy="10" r="7" class="node"></circle>
|
|
662
|
+
</svg>
|
|
663
|
+
,<em>面积</em>随链接数量缩放。
|
|
664
|
+
|
|
665
|
+
<p></p>
|
|
666
|
+
输入文本以搜索<em>属性包含该词</em>的簇和节点。<br />
|
|
667
|
+
例如,输入 <code>MSM</code> 将高亮显示任何数据字段中包含 'MSM' 的节点和/或簇。
|
|
668
|
+
<br />
|
|
669
|
+
输入空格分隔的词(<code>MSM IDU</code>)可搜索<b>任一</b>词。<br />
|
|
670
|
+
输入引号中的词(<code>\"male\"</code>)将搜索<b>精确</b>匹配的词。<br />
|
|
671
|
+
输入 <code><0.01</code> 可搜索具有 0.01 (1%) 或更短边的节点。任何正阈值都有效。
|
|
672
|
+
<br />
|
|
673
|
+
<p></p>
|
|
674
|
+
匹配的节点
|
|
675
|
+
<svg width="20" height="20" style="vertical-align: bottom; display: inline">
|
|
676
|
+
<circle cx="10" cy="10" r="7" class="node selected_object"></circle>
|
|
677
|
+
</svg><br />
|
|
678
|
+
25% 节点匹配该词的簇
|
|
679
|
+
<svg width="28" height="28" style="vertical-align: bottom; display: inline">
|
|
680
|
+
<circle cx="14" cy="14" r="8" class="cluster"></circle>
|
|
681
|
+
<path d="M 2 14 A 12 12 0 0 1 14 2" style="fill: none; stroke: #337ab7; stroke-width: 3px" />
|
|
682
|
+
</svg><br />
|
|
683
|
+
75% 节点匹配该词的簇
|
|
684
|
+
<svg width="28" height="28" style="vertical-align: bottom; display: inline">
|
|
685
|
+
<circle cx="14" cy="14" r="8" class="cluster"></circle>
|
|
686
|
+
<path d="M 2 14 A 12 12 0 1 1 14 26" style="fill: none; stroke: #337ab7; stroke-width: 3px" />
|
|
687
|
+
</svg>
|
|
688
|
+
<p></p>
|
|
689
|
+
|
|
690
|
+
使用 <code>隐藏其他</code> 复选框可自动隐藏不匹配搜索词的所有簇/节点
|
|
691
|
+
<br />
|
|
692
|
+
使用 <code>显示小簇</code> 复选框可显示为清晰起见而隐藏的小簇
|
|
693
|
+
</div>
|
|
694
|
+
</div>
|
|
695
|
+
|
|
696
|
+
<div id="colorPickerOption">
|
|
697
|
+
<div id="colorPickerRow" class="row collapse"></div>
|
|
698
|
+
</div>
|
|
699
|
+
|
|
700
|
+
<!-- Main SVG -->
|
|
701
|
+
<div id="network_tag">
|
|
702
|
+
<div class="my_progress">
|
|
703
|
+
<div class="progress-bar progress-bar-striped disabled" role="progressbar" aria-valuenow="100"
|
|
704
|
+
aria-valuemin="0" aria-valuemax="100" style="width: 100%; height: 50px">
|
|
705
|
+
正在加载网络
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
</div>
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
</div>
|
|
712
|
+
|
|
713
|
+
<div id="trace-graph" class="tab-pane">
|
|
714
|
+
<div class="row">
|
|
715
|
+
<div class="col-lg-6">
|
|
716
|
+
<p class="lead">网络统计</p>
|
|
717
|
+
<table class="table table-striped table-condensed table-responsive" id="graph_summary_table"></table>
|
|
718
|
+
</div>
|
|
719
|
+
<div class="col-lg-6">
|
|
720
|
+
<p id="histogram_label" class="lead"></p>
|
|
721
|
+
<div class="row" id="histogram_tag" style="margin-left: 5px"></div>
|
|
722
|
+
</div>
|
|
723
|
+
</div>
|
|
724
|
+
</div>
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
<div id="trace-clusters" class="tab-pane">
|
|
728
|
+
<div class="row">
|
|
729
|
+
<div class="col-lg-12">
|
|
730
|
+
<span class="pull-right" id="cluster-table-export"> </span>
|
|
731
|
+
<p class="lead">簇</p>
|
|
732
|
+
<table class="table table-striped table-condensed table-hover" id="cluster_table"></table>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
<div id="trace-nodes" class="tab-pane">
|
|
739
|
+
<div class="row">
|
|
740
|
+
<div class="col-lg-12">
|
|
741
|
+
<span class="pull-right" id="node-table-export"> </span>
|
|
742
|
+
<p class="lead">关联个体</p>
|
|
743
|
+
<table class="table table-striped table-condensed table-hover" id="node_table"></table>
|
|
744
|
+
</div>
|
|
745
|
+
</div>
|
|
746
|
+
</div>
|
|
747
|
+
|
|
748
|
+
<div id="trace-attributes" class="tab-pane">
|
|
749
|
+
<div class="hivtrace-attributes-container">
|
|
750
|
+
<!-- 顶部:属性选择器(横向 pills) -->
|
|
751
|
+
<div class="hivtrace-attributes-header">
|
|
752
|
+
<div class="hivtrace-pill-row" data-hivtrace-ui-role="attributes_cat"></div>
|
|
753
|
+
</div>
|
|
754
|
+
|
|
755
|
+
<!-- 下方:左侧图表 + 右侧表格 -->
|
|
756
|
+
<div class="hivtrace-attributes-content">
|
|
757
|
+
<!-- 左侧:可视化区域(弦图/散点图) -->
|
|
758
|
+
<div class="hivtrace-viz-panel" data-hivtrace-ui-role="aux_svg_holder_enclosed" style="display: none">
|
|
759
|
+
<div class="hivtrace-panel-toolbar">
|
|
760
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" id="network_pairwise_chord_legend">
|
|
761
|
+
<i class="fa fa-question"></i>
|
|
762
|
+
</button>
|
|
763
|
+
</div>
|
|
764
|
+
<div class="hivtrace-viz-wrapper" data-hivtrace-ui-role="aux_svg_holder"></div>
|
|
765
|
+
</div>
|
|
766
|
+
|
|
767
|
+
<!-- 右侧:表格区域 -->
|
|
768
|
+
<div class="hivtrace-table-panel" data-hivtrace-ui-role="attribute_table_enclosed"
|
|
769
|
+
style="display: none">
|
|
770
|
+
<div class="hivtrace-panel-toolbar">
|
|
771
|
+
<label class="hivtrace-toggle-label">
|
|
772
|
+
<input type="checkbox" data-hivtrace-ui-role="pairwise_table_pecentage" />
|
|
773
|
+
<span>显示为百分比</span>
|
|
774
|
+
</label>
|
|
775
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" id="network_pairwise_table_legend">
|
|
776
|
+
<i class="fa fa-question"></i>
|
|
777
|
+
</button>
|
|
778
|
+
</div>
|
|
779
|
+
<div class="hivtrace-table-wrapper">
|
|
780
|
+
<table class="table table-striped table-condensed table-hover"
|
|
781
|
+
data-hivtrace-ui-role="attribute_table">
|
|
782
|
+
</table>
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
</div>
|
|
786
|
+
</div>
|
|
787
|
+
</div>
|
|
788
|
+
</div>
|
|
789
|
+
</div>
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
<img class="hidden" id="hyphy-chart-image" />
|
|
793
|
+
<canvas class="hidden" id="hyphy-chart-canvas"></canvas>
|
|
794
|
+
|
|
795
|
+
</div> <!-- 关闭内容容器 -->
|
|
796
|
+
</div> <!-- 关闭 #hivtrace-main -->
|
|
797
|
+
|
|
798
|
+
<script src="./hivtrace.js"></script>
|
|
799
|
+
<!-- <script src="dist/hivtrace.es.js"></script> -->
|
|
800
|
+
|
|
801
|
+
<script>
|
|
802
|
+
var network_container = "#network_tag",
|
|
803
|
+
network_status_string = "#network_status_string",
|
|
804
|
+
network_warning = "#main-warning",
|
|
805
|
+
histogram_tag = "#histogram_tag",
|
|
806
|
+
histogram_label = "#histogram_label",
|
|
807
|
+
button_bar_prefix = "network_ui_bar",
|
|
808
|
+
csvexport_label = "#csvexport",
|
|
809
|
+
fasta_export_label = "#fasta-export",
|
|
810
|
+
filter_edges_toggle = "#network_ui_bar_toggle_filter_edges",
|
|
811
|
+
graph_summary_tag = "#graph_summary_table",
|
|
812
|
+
cluster_table = "#cluster_table",
|
|
813
|
+
subcluster_table = "#subcluster_table",
|
|
814
|
+
parent_container = "#top_level_tab_container",
|
|
815
|
+
node_table = "#node_table",
|
|
816
|
+
user_graph,
|
|
817
|
+
countryCenersObject = null,
|
|
818
|
+
countryOutlines = null;
|
|
819
|
+
|
|
820
|
+
function HandleAppError(err_string) {
|
|
821
|
+
showLoadingError(err_string);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
var init = function (data, options) {
|
|
825
|
+
try {
|
|
826
|
+
var graph = "trace_results" in data ? data.trace_results : data;
|
|
827
|
+
var attributes = null;
|
|
828
|
+
|
|
829
|
+
// 从传入的options中获取配置
|
|
830
|
+
var enableClusterTracking = options && options.enableClusterTracking === true;
|
|
831
|
+
var expandClusters = options && Array.isArray(options.expand) ? options.expand : [];
|
|
832
|
+
|
|
833
|
+
// 获取基因距离阈值并动态更新簇标签
|
|
834
|
+
var threshold = options && typeof options.threshold === 'number' ? options.threshold : 0.015;
|
|
835
|
+
var thresholdPercent = (threshold * 100).toFixed(1).replace(/\.0$/, '');
|
|
836
|
+
// 使用硬编码文本,因为 embed.html 模板不在 webpack 编译范围内
|
|
837
|
+
var clusterTabLabel = "簇 (" + thresholdPercent + "%)";
|
|
838
|
+
var clusterTabLink = document.querySelector('#clusters-tab a');
|
|
839
|
+
if (clusterTabLink) {
|
|
840
|
+
clusterTabLink.textContent = clusterTabLabel;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
user_graph = new hivtrace.clusterNetwork(
|
|
844
|
+
graph,
|
|
845
|
+
network_container,
|
|
846
|
+
network_status_string,
|
|
847
|
+
network_warning,
|
|
848
|
+
button_bar_prefix,
|
|
849
|
+
attributes,
|
|
850
|
+
filter_edges_toggle,
|
|
851
|
+
cluster_table,
|
|
852
|
+
node_table,
|
|
853
|
+
parent_container,
|
|
854
|
+
{
|
|
855
|
+
no_cdc: true,
|
|
856
|
+
"subcluster-table": subcluster_table,
|
|
857
|
+
//"country-centers": countryCentersObject,
|
|
858
|
+
//"country-outlines": countryOutlines,
|
|
859
|
+
seguro: true,
|
|
860
|
+
"subcluster-table": subcluster_table,
|
|
861
|
+
expand: expandClusters
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
if (user_graph.is_empty()) {
|
|
866
|
+
HandleAppError(
|
|
867
|
+
"This network contains no clusters and cannot be displayed"
|
|
868
|
+
);
|
|
869
|
+
} else {
|
|
870
|
+
hivtrace.histogramDistances(graph, histogram_tag, histogram_label);
|
|
871
|
+
hivtrace.graphSummary(user_graph, graph_summary_tag);
|
|
872
|
+
|
|
873
|
+
// 可配置的追踪簇回调功能 - 基于传入参数决定是否启用
|
|
874
|
+
if (enableClusterTracking) {
|
|
875
|
+
user_graph.onTrackCluster = function (clusterInfo, allData) {
|
|
876
|
+
try {
|
|
877
|
+
// 直接提取需要的数据,避免复杂的数据清理
|
|
878
|
+
const essentialClusterData = {
|
|
879
|
+
cluster_id: clusterInfo?.cluster_id ?? clusterInfo?.id ?? undefined,
|
|
880
|
+
node_count: clusterInfo?.node_count ?? (Array.isArray(clusterInfo?.nodes) ? clusterInfo.nodes.length : 0),
|
|
881
|
+
cluster_size: typeof clusterInfo?.cluster_size === 'number' ? clusterInfo.cluster_size : undefined,
|
|
882
|
+
position: {
|
|
883
|
+
x: typeof clusterInfo?.x === 'number' ? clusterInfo.x : undefined,
|
|
884
|
+
y: typeof clusterInfo?.y === 'number' ? clusterInfo.y : undefined
|
|
885
|
+
},
|
|
886
|
+
state: {
|
|
887
|
+
expanded: Boolean(clusterInfo?.expanded),
|
|
888
|
+
fixed: Boolean(clusterInfo?.fixed)
|
|
889
|
+
},
|
|
890
|
+
// 只提取节点ID列表,避免传递复杂对象
|
|
891
|
+
node_ids: Array.isArray(clusterInfo?.nodes)
|
|
892
|
+
? clusterInfo.nodes.slice(0, 20).map(node => node?.id).filter(Boolean)
|
|
893
|
+
: []
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
// 提取网络统计信息
|
|
897
|
+
const networkStats = {
|
|
898
|
+
total_nodes: allData?.network_info?.node_count ?? user_graph?.nodes?.length ?? 0,
|
|
899
|
+
total_edges: allData?.network_info?.edge_count ?? user_graph?.edges?.length ?? 0,
|
|
900
|
+
total_clusters: allData?.network_info?.cluster_count ?? 0
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
// 发送轻量级、精确的数据
|
|
904
|
+
window.parent.postMessage({
|
|
905
|
+
type: 'TRACK_CLUSTER',
|
|
906
|
+
clusterInfo: essentialClusterData,
|
|
907
|
+
allData: { network_info: networkStats }
|
|
908
|
+
}, '*');
|
|
909
|
+
|
|
910
|
+
} catch (error) {
|
|
911
|
+
console.warn('簇追踪回调处理失败:', error.message);
|
|
912
|
+
|
|
913
|
+
// 最小化降级数据
|
|
914
|
+
window.parent.postMessage({
|
|
915
|
+
type: 'TRACK_CLUSTER',
|
|
916
|
+
clusterInfo: {
|
|
917
|
+
cluster_id: 'fallback',
|
|
918
|
+
node_count: 0,
|
|
919
|
+
cluster_size: undefined,
|
|
920
|
+
position: { x: undefined, y: undefined },
|
|
921
|
+
state: { expanded: false, fixed: false },
|
|
922
|
+
node_ids: []
|
|
923
|
+
},
|
|
924
|
+
allData: {
|
|
925
|
+
network_info: {
|
|
926
|
+
total_nodes: 0,
|
|
927
|
+
total_edges: 0,
|
|
928
|
+
total_clusters: 0
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}, '*');
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
[
|
|
937
|
+
"#main-tab",
|
|
938
|
+
"#graph-tab",
|
|
939
|
+
"#clusters-tab",
|
|
940
|
+
"#subclusters-tab",
|
|
941
|
+
"#nodes-tab",
|
|
942
|
+
"#attributes-tab",
|
|
943
|
+
].forEach(function (tab) {
|
|
944
|
+
d3.select(tab).classed("disabled", false);
|
|
945
|
+
});
|
|
946
|
+
d3.select(parent_container)
|
|
947
|
+
.selectAll("li")
|
|
948
|
+
.selectAll("a")
|
|
949
|
+
.each(function (d) {
|
|
950
|
+
d3.select(d3.select(this).attr("href")).style("display", null);
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
hivtrace.misc.export_table_to_text(
|
|
954
|
+
"#cluster-table-export",
|
|
955
|
+
cluster_table
|
|
956
|
+
);
|
|
957
|
+
hivtrace.misc.export_table_to_text(
|
|
958
|
+
"#subcluster-table-export",
|
|
959
|
+
subcluster_table
|
|
960
|
+
);
|
|
961
|
+
hivtrace.misc.export_table_to_text(
|
|
962
|
+
"#node-table-export",
|
|
963
|
+
node_table
|
|
964
|
+
);
|
|
965
|
+
|
|
966
|
+
$("#main-tab a[data-toggle='tab']").on(
|
|
967
|
+
"shown.bs.tab",
|
|
968
|
+
function (e) {
|
|
969
|
+
if (user_graph.needs_an_update) {
|
|
970
|
+
user_graph.update(false, 0.5);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
|
|
975
|
+
$("#clusters-tab a[data-toggle='tab']").on(
|
|
976
|
+
"shown.bs.tab",
|
|
977
|
+
function (e) {
|
|
978
|
+
user_graph.update_volatile_elements(d3.select(cluster_table));
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
$("#nodes-tab a[data-toggle='tab']").on(
|
|
983
|
+
"shown.bs.tab",
|
|
984
|
+
function (e) {
|
|
985
|
+
user_graph.update_volatile_elements(d3.select(node_table));
|
|
986
|
+
}
|
|
987
|
+
);
|
|
988
|
+
|
|
989
|
+
}
|
|
990
|
+
document
|
|
991
|
+
.getElementById("min_cluster_size_input")
|
|
992
|
+
.setAttribute("placeholder", user_graph.minimum_cluster_size);
|
|
993
|
+
|
|
994
|
+
// 加载成功:隐藏加载态,显示主内容
|
|
995
|
+
document.getElementById('hivtrace-loading').classList.add('hidden');
|
|
996
|
+
document.getElementById('hivtrace-main').classList.add('loaded');
|
|
997
|
+
|
|
998
|
+
// 通知父窗口加载完成
|
|
999
|
+
window.parent.postMessage({ type: 'HIVTRACE_READY' }, '*');
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
console.log(e);
|
|
1002
|
+
// 显示错误态
|
|
1003
|
+
showLoadingError("网络加载失败: " + e.message);
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
// 更新网络数据的函数,避免重新init
|
|
1008
|
+
var updateNetworkData = function (newGraphData, options) {
|
|
1009
|
+
try {
|
|
1010
|
+
if (!user_graph || user_graph.is_empty()) {
|
|
1011
|
+
// 如果还没有初始化,使用完整初始化
|
|
1012
|
+
init(newGraphData, options);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
var newGraph = "trace_results" in newGraphData ? newGraphData.trace_results : newGraphData;
|
|
1017
|
+
|
|
1018
|
+
// 直接替换数据
|
|
1019
|
+
user_graph.json = newGraph;
|
|
1020
|
+
|
|
1021
|
+
// 执行必要的更新
|
|
1022
|
+
user_graph.update(false, 0.4);
|
|
1023
|
+
|
|
1024
|
+
if (typeof user_graph.update_clusters_with_injected_nodes === 'function') {
|
|
1025
|
+
user_graph.update_clusters_with_injected_nodes();
|
|
1026
|
+
}
|
|
1027
|
+
if (typeof user_graph.update_volatile_elements === 'function') {
|
|
1028
|
+
user_graph.update_volatile_elements(user_graph.cluster_table);
|
|
1029
|
+
}
|
|
1030
|
+
if (typeof user_graph.redraw_tables === 'function') {
|
|
1031
|
+
user_graph.redraw_tables();
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// 动态更新完成后通知父级
|
|
1035
|
+
window.parent.postMessage({ type: 'HIVTRACE_READY' }, '*');
|
|
1036
|
+
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
console.error("动态数据更新失败,回退到完整初始化:", error);
|
|
1039
|
+
// 如果更新失败,回退到完整初始化
|
|
1040
|
+
init(newGraphData, options);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
// 显示错误态的辅助函数
|
|
1045
|
+
function showLoadingError(message) {
|
|
1046
|
+
var overlay = document.getElementById('hivtrace-loading');
|
|
1047
|
+
overlay.classList.add('error');
|
|
1048
|
+
overlay.innerHTML = `
|
|
1049
|
+
<i class="fa fa-exclamation-triangle hivtrace-error-icon"></i>
|
|
1050
|
+
<div class="hivtrace-error-message">${message}</div>
|
|
1051
|
+
<button class="hivtrace-retry-btn" onclick="location.reload()">重试</button>
|
|
1052
|
+
`;
|
|
1053
|
+
window.parent.postMessage({ type: 'ERROR', message: message }, '*');
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
function in_progress() {
|
|
1058
|
+
return $(".progress").length > 0;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// 全屏功能管理器(在 document.ready 外部定义以确保变量提升)
|
|
1062
|
+
var FullscreenManager = {
|
|
1063
|
+
isFullscreen: false,
|
|
1064
|
+
|
|
1065
|
+
isSupported: function () {
|
|
1066
|
+
return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled ||
|
|
1067
|
+
document.mozFullScreenEnabled || document.msFullscreenEnabled);
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
checkFullscreenStatus: function () {
|
|
1071
|
+
return !!(document.fullscreenElement || document.webkitFullscreenElement ||
|
|
1072
|
+
document.mozFullScreenElement || document.msFullscreenElement);
|
|
1073
|
+
},
|
|
1074
|
+
|
|
1075
|
+
enterFullscreen: function () {
|
|
1076
|
+
var elem = document.documentElement;
|
|
1077
|
+
if (elem.requestFullscreen) {
|
|
1078
|
+
elem.requestFullscreen();
|
|
1079
|
+
} else if (elem.webkitRequestFullscreen) {
|
|
1080
|
+
elem.webkitRequestFullscreen();
|
|
1081
|
+
} else if (elem.mozRequestFullScreen) {
|
|
1082
|
+
elem.mozRequestFullScreen();
|
|
1083
|
+
} else if (elem.msRequestFullscreen) {
|
|
1084
|
+
elem.msRequestFullscreen();
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
|
|
1088
|
+
exitFullscreen: function () {
|
|
1089
|
+
if (document.exitFullscreen) {
|
|
1090
|
+
document.exitFullscreen();
|
|
1091
|
+
} else if (document.webkitExitFullscreen) {
|
|
1092
|
+
document.webkitExitFullscreen();
|
|
1093
|
+
} else if (document.mozCancelFullScreen) {
|
|
1094
|
+
document.mozCancelFullScreen();
|
|
1095
|
+
} else if (document.msExitFullscreen) {
|
|
1096
|
+
document.msExitFullscreen();
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
|
|
1100
|
+
toggle: function () {
|
|
1101
|
+
if (this.checkFullscreenStatus()) {
|
|
1102
|
+
this.exitFullscreen();
|
|
1103
|
+
} else {
|
|
1104
|
+
this.enterFullscreen();
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
|
|
1108
|
+
updateButton: function () {
|
|
1109
|
+
var isFullscreen = this.checkFullscreenStatus();
|
|
1110
|
+
var icon = document.getElementById('fullscreen_icon');
|
|
1111
|
+
var button = document.getElementById('fullscreen_toggle_btn');
|
|
1112
|
+
var container = document.getElementById('hivtrace-main');
|
|
1113
|
+
|
|
1114
|
+
if (container) {
|
|
1115
|
+
if (isFullscreen) {
|
|
1116
|
+
container.style.height = '100vh';
|
|
1117
|
+
container.style.width = '100vw';
|
|
1118
|
+
container.style.padding = '0px 4px';
|
|
1119
|
+
container.style.margin = '0';
|
|
1120
|
+
} else {
|
|
1121
|
+
container.style.height = '';
|
|
1122
|
+
container.style.width = '';
|
|
1123
|
+
container.style.padding = '';
|
|
1124
|
+
container.style.margin = '';
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (icon && button) {
|
|
1129
|
+
if (isFullscreen) {
|
|
1130
|
+
icon.className = 'fa fa-compress';
|
|
1131
|
+
button.title = '退出全屏';
|
|
1132
|
+
} else {
|
|
1133
|
+
icon.className = 'fa fa-expand';
|
|
1134
|
+
button.title = '进入全屏';
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// 通知父级框架全屏状态变化
|
|
1139
|
+
if (this.isFullscreen !== isFullscreen) {
|
|
1140
|
+
window.parent.postMessage({
|
|
1141
|
+
type: 'FULLSCREEN_CHANGE',
|
|
1142
|
+
isFullscreen: isFullscreen
|
|
1143
|
+
}, '*');
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
this.isFullscreen = isFullscreen;
|
|
1147
|
+
},
|
|
1148
|
+
|
|
1149
|
+
init: function () {
|
|
1150
|
+
var self = this;
|
|
1151
|
+
var button = document.getElementById('fullscreen_toggle_btn');
|
|
1152
|
+
|
|
1153
|
+
if (!this.isSupported()) {
|
|
1154
|
+
var span = document.getElementById('fullscreen_toggle_span');
|
|
1155
|
+
if (span) {
|
|
1156
|
+
span.style.display = 'none';
|
|
1157
|
+
}
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (button) {
|
|
1162
|
+
button.addEventListener('click', function (e) {
|
|
1163
|
+
e.preventDefault();
|
|
1164
|
+
self.toggle();
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
document.addEventListener('fullscreenchange', function () { self.updateButton(); });
|
|
1169
|
+
document.addEventListener('webkitfullscreenchange', function () { self.updateButton(); });
|
|
1170
|
+
document.addEventListener('mozfullscreenchange', function () { self.updateButton(); });
|
|
1171
|
+
document.addEventListener('MSFullscreenChange', function () { self.updateButton(); });
|
|
1172
|
+
|
|
1173
|
+
this.updateButton();
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
$(document).ready(function () {
|
|
1178
|
+
// 初始化全屏功能
|
|
1179
|
+
FullscreenManager.init();
|
|
1180
|
+
|
|
1181
|
+
// 通知父窗口准备就绪
|
|
1182
|
+
window.parent.postMessage({ type: 'HIVTRACE_INIT_READY' }, '*');
|
|
1183
|
+
|
|
1184
|
+
// 监听来自父窗口的消息
|
|
1185
|
+
window.addEventListener('message', function (event) {
|
|
1186
|
+
var data = event.data;
|
|
1187
|
+
if (!data || !data.type) return;
|
|
1188
|
+
|
|
1189
|
+
if (data.type === 'HIVTRACE_DATA') {
|
|
1190
|
+
init(data.graphData, data.options);
|
|
1191
|
+
} else if (data.type === 'HIVTRACE_UPDATE_DATA') {
|
|
1192
|
+
updateNetworkData(data.graphData, data.options);
|
|
1193
|
+
} else if (data.type === 'HIVTRACE_ERROR') {
|
|
1194
|
+
// 处理来自宿主应用的错误(如 API 请求失败)
|
|
1195
|
+
showLoadingError(data.message || '数据加载失败');
|
|
1196
|
+
} else if (data.type === 'FULLSCREEN_TOGGLE') {
|
|
1197
|
+
// 处理来自父级的全屏切换请求
|
|
1198
|
+
if (FullscreenManager && typeof FullscreenManager.toggle === 'function') {
|
|
1199
|
+
FullscreenManager.toggle();
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
$("#network_pairwise_chord_legend").popover({
|
|
1205
|
+
html: true,
|
|
1206
|
+
trigger: "click",
|
|
1207
|
+
placement: "left",
|
|
1208
|
+
container: "body",
|
|
1209
|
+
content:
|
|
1210
|
+
"<div style = 'width : 250px'>\
|
|
1211
|
+
本面板将显示<b>弦图</b>(用于分类值)\
|
|
1212
|
+
或 <b>散点图</b>(用于连续值)。它们用于显示跨链接的节点属性配对。\
|
|
1213
|
+
将鼠标悬停在特定颜色上可显示它在<b>弦图</b>中对应的属性。\
|
|
1214
|
+
将鼠标悬停在特定点上可显示它在<b>直方图</b>中对应的链接。<p/> \
|
|
1215
|
+
为更好理解<b>弦图</b>,假设一个有 100 条链接的网络。40 条链接连接男性与男性,\
|
|
1216
|
+
10 条链接连接女性与女性,50 条链接连接男性与女性。\
|
|
1217
|
+
男性将被分配 (40 x 2 + 50) / 200 = 0.65 的总圆周(饼切片大小)。\
|
|
1218
|
+
男性与女性之间的连接(以男性为焦点)将获得 50 / (50+40) = 5/9 的权重。\
|
|
1219
|
+
女性与男性之间的连接(以女性为焦点)将获得 50 / (50+10) = 5/6 的权重。\
|
|
1220
|
+
</div>\
|
|
1221
|
+
",
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
$("#network_pairwise_table_legend").popover({
|
|
1225
|
+
html: true,
|
|
1226
|
+
trigger: "click",
|
|
1227
|
+
placement: "left",
|
|
1228
|
+
container: "body",
|
|
1229
|
+
content:
|
|
1230
|
+
'<div style = \'width : 250px\'>\
|
|
1231
|
+
此表显示每对属性值之间存在多少连接。例如,如果\
|
|
1232
|
+
一条链接连接一个"男性"节点和一个"女性"节点,这条链接将\
|
|
1233
|
+
在表格的"男性/女性"和"女性/男性"单元格中各贡献 1 次计数。\
|
|
1234
|
+
连接"男性"节点与"男性"节点的链接将在"男性/男性"单元格中贡献 2 次计数。\
|
|
1235
|
+
</div>\
|
|
1236
|
+
',
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
$("#network_pairwise_chart_legend").popover({
|
|
1240
|
+
html: true,
|
|
1241
|
+
trigger: "click",
|
|
1242
|
+
placement: "bottom",
|
|
1243
|
+
content:
|
|
1244
|
+
'<div style = \'width : 250px\'>\
|
|
1245
|
+
此表显示每对属性值之间存在多少连接。例如,如果\
|
|
1246
|
+
一条链接连接一个"男性"节点和一个"女性"节点,这条链接将\
|
|
1247
|
+
在表格的"男性/女性"和"女性/男性"单元格中各贡献 1 次计数。\
|
|
1248
|
+
连接"男性"节点与"男性"节点的链接将在"男性/男性"单元格中贡献 2 次计数。\
|
|
1249
|
+
</div>\
|
|
1250
|
+
',
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
$("#csv_node_file_help").popover({
|
|
1254
|
+
html: true,
|
|
1255
|
+
trigger: "click",
|
|
1256
|
+
placement: "right",
|
|
1257
|
+
content:
|
|
1258
|
+
"<div style = 'width : 250px'>\
|
|
1259
|
+
示例内容<br>\
|
|
1260
|
+
<code>Index,Heroin_Use,Internet_Dating</code><br>\
|
|
1261
|
+
<code>SocialID669,No,Yes</code><br>\
|
|
1262
|
+
<code>SocialID563,Yes,Yes</code><br>\
|
|
1263
|
+
</code><br>\
|
|
1264
|
+
必须有 'Index' 列,该列将映射到现有网络节点 ID。\
|
|
1265
|
+
</div>\
|
|
1266
|
+
",
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
$("#csv_edge_file_help").popover({
|
|
1270
|
+
html: true,
|
|
1271
|
+
trigger: "click",
|
|
1272
|
+
placement: "right",
|
|
1273
|
+
content:
|
|
1274
|
+
"<div style = 'width : 250px'>\
|
|
1275
|
+
示例内容<br>\
|
|
1276
|
+
<code>Index,Partner,Contact</code><br>\
|
|
1277
|
+
<code>SocialID39,SocialID542,IDU</code><br>\
|
|
1278
|
+
<code>SocialID38,SocialID139,MSM</code><br>\
|
|
1279
|
+
</code><br>\
|
|
1280
|
+
必须存在所有三个列出的列。Index 和 Partner ID 必须在分子网络或节点属性文件中。\
|
|
1281
|
+
</div>\
|
|
1282
|
+
",
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
$("#network_pairwise_table_legend").on("click", function (e) {
|
|
1286
|
+
e.preventDefault();
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
$("#network_ui_search_help").popover({
|
|
1290
|
+
html: true,
|
|
1291
|
+
trigger: "hover click",
|
|
1292
|
+
placement: "bottom",
|
|
1293
|
+
content:
|
|
1294
|
+
"<div style = 'width : 250px'>\
|
|
1295
|
+
输入文本以搜索<em>属性包含该词</em>的簇和节点。<p/>\
|
|
1296
|
+
例如,输入 <code>MSM</code> 将高亮显示任何数据字段中包含 'MSM' 的节点和/或簇。<p/>\
|
|
1297
|
+
输入空格分隔的词(<code>MSM IDU</code>)可搜索<b>任一</b>词。<p/>\
|
|
1298
|
+
输入引号中的词(<code>\"male\"</code>)将搜索<b>精确</b>匹配的词。<p/>\
|
|
1299
|
+
输入 <code><0.01</code> 可搜索具有 0.01 (1%) 或更短边的节点。任何正阈值都有效。<p/>\
|
|
1300
|
+
匹配的节点 <svg width = '20' height = '20' style = 'vertical-align: bottom; display: inline'><circle cx = '10' cy = '10' r = '7' class = 'node selected_object'> </circle></svg><p/>\
|
|
1301
|
+
25% 节点匹配该词的簇 <svg width = '28' height = '28' style = 'vertical-align: bottom; display: inline'><circle cx = '14' cy = '14' r = '8' class = 'cluster'> </circle>\
|
|
1302
|
+
<path d = 'M 2 14 A 12 12 0 0 1 14 2'/ style = 'fill: none; stroke: #337AB7; stroke-width: 3px;'>\
|
|
1303
|
+
</svg><p/> \
|
|
1304
|
+
75% 节点匹配该词的簇 <svg width = '28' height = '28' style = 'vertical-align: bottom; display: inline'><circle cx = '14' cy = '14' r = '8' class = 'cluster'> </circle>\
|
|
1305
|
+
<path d = 'M 2 14 A 12 12 0 1 1 14 26'/ style = 'fill: none; stroke: #337AB7; stroke-width: 3px;'>\
|
|
1306
|
+
</svg><p/> \
|
|
1307
|
+
使用 <code>隐藏其他</code> 复选框可自动隐藏不匹配搜索词的所有簇/节点\
|
|
1308
|
+
<p/>\
|
|
1309
|
+
更改 <code>最小簇大小</code> 字段可显示或隐藏簇以提高清晰度\
|
|
1310
|
+
</div>\
|
|
1311
|
+
",
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
});
|
|
1315
|
+
</script>
|
|
1316
|
+
</body>
|
|
1317
|
+
|
|
1318
|
+
</html>
|