hesamation commited on
Commit
675149c
·
1 Parent(s): 35e5b82

graph fixed, working on click even

Browse files
Files changed (4) hide show
  1. css/style.css +71 -62
  2. daily-paper-atlas/js/main.js +354 -0
  3. index.html +1 -29
  4. js/main.js +314 -203
css/style.css CHANGED
@@ -218,33 +218,30 @@ canvas#sigma_bg_1 {
218
 
219
  #mainpanel {
220
  position: absolute;
221
- top: 0;
222
- left: 0;
223
- bottom: auto;
224
- width: 240px;
225
- height: auto;
 
226
  z-index: 20;
227
- background: rgba(255, 255, 255, 0.95);
228
- border-right: 1px solid #ccc;
229
- box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
230
- overflow: auto;
231
- padding: 0;
232
  }
233
 
234
  #mainpanel .col {
235
- padding: 15px;
236
- border-bottom: 1px solid #eee;
237
  }
238
 
239
  #title {
 
240
  font-weight: bold;
241
- text-align: left;
242
- font-size: 18px;
243
  margin-bottom: 5px;
244
  }
245
 
246
  #titletext {
247
- padding: 5px 0;
 
248
  }
249
 
250
  .info {
@@ -261,31 +258,36 @@ canvas#sigma_bg_1 {
261
  }
262
 
263
  #legend {
264
- padding: 10px 0;
 
 
265
  }
266
 
267
- .legenditem {
268
  display: flex;
269
  align-items: center;
270
  margin-bottom: 5px;
271
  }
272
 
273
- .legendcolor {
274
- display: inline-block;
275
- width: 12px;
276
- height: 12px;
277
  margin-right: 8px;
278
- border-radius: 50%;
279
  }
280
 
281
- .box {
282
- margin-bottom: 10px;
 
 
 
 
283
  }
284
 
285
- h2 {
286
- font-size: 14px;
287
- font-weight: bold;
288
- margin-bottom: 10px;
289
  }
290
 
291
  #search {
@@ -301,13 +303,14 @@ h2 {
301
  }
302
 
303
  #search-button {
304
- display: inline-block;
305
- background: #f5f5f5;
306
- border: 1px solid #ccc;
307
- padding: 3px 8px;
 
 
 
308
  cursor: pointer;
309
- margin-left: 5px;
310
- vertical-align: middle;
311
  }
312
 
313
  .state {
@@ -321,21 +324,17 @@ h2 {
321
 
322
  .results {
323
  margin-top: 10px;
324
- border: 1px solid #eee;
325
- max-height: 200px;
326
- overflow-y: auto;
327
  }
328
 
329
  .results a {
330
  display: block;
331
- padding: 5px;
 
332
  text-decoration: none;
333
- color: #000;
334
- border-bottom: 1px solid #eee;
335
  }
336
 
337
  .results a:hover {
338
- background: #f5f5f5;
339
  }
340
 
341
  .select-wrapper {
@@ -404,23 +403,12 @@ h2 {
404
  }
405
 
406
  #colorLegend {
 
 
 
407
  margin-top: 10px;
408
  }
409
 
410
- #colorLegend div {
411
- display: flex;
412
- align-items: center;
413
- margin-bottom: 5px;
414
- }
415
-
416
- #colorLegend span {
417
- display: inline-block;
418
- width: 12px;
419
- height: 12px;
420
- border-radius: 50%;
421
- margin-right: 8px;
422
- }
423
-
424
  #zoom {
425
  position: absolute;
426
  bottom: 40px;
@@ -464,16 +452,11 @@ h2 {
464
  }
465
 
466
  #key-features {
467
- padding-top: 10px;
468
  }
469
 
470
  #key-features ul {
471
- margin-left: 20px;
472
- margin-top: 5px;
473
- }
474
-
475
- #key-features li {
476
- margin-bottom: 5px;
477
  }
478
 
479
  #node-info {
@@ -486,4 +469,30 @@ h2 {
486
  z-index: 10;
487
  display: none;
488
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
 
218
 
219
  #mainpanel {
220
  position: absolute;
221
+ top: 10px;
222
+ left: 10px;
223
+ width: 250px;
224
+ padding: 10px;
225
+ background: rgba(255, 255, 255, 0.9);
226
+ box-shadow: 0 1px 3px rgba(0,0,0,0.2);
227
  z-index: 20;
228
+ max-height: calc(100% - 40px);
229
+ overflow-y: auto;
 
 
 
230
  }
231
 
232
  #mainpanel .col {
233
+ margin-bottom: 20px;
 
234
  }
235
 
236
  #title {
237
+ font-size: 24px;
238
  font-weight: bold;
 
 
239
  margin-bottom: 5px;
240
  }
241
 
242
  #titletext {
243
+ margin-bottom: 15px;
244
+ line-height: 1.5;
245
  }
246
 
247
  .info {
 
258
  }
259
 
260
  #legend {
261
+ margin-top: 15px;
262
+ margin-bottom: 15px;
263
+ display: block;
264
  }
265
 
266
+ .legend-item {
267
  display: flex;
268
  align-items: center;
269
  margin-bottom: 5px;
270
  }
271
 
272
+ .legend-color {
273
+ width: 16px;
274
+ height: 16px;
275
+ border-radius: 3px;
276
  margin-right: 8px;
277
+ display: inline-block;
278
  }
279
 
280
+ .legend-line {
281
+ width: 24px;
282
+ height: 2px;
283
+ background-color: #666;
284
+ margin-right: 8px;
285
+ display: inline-block;
286
  }
287
 
288
+ .legend-label {
289
+ display: inline-block;
290
+ font-size: 13px;
 
291
  }
292
 
293
  #search {
 
303
  }
304
 
305
  #search-button {
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: center;
309
+ width: 30px;
310
+ background-color: #1f77b4;
311
+ color: white;
312
+ border-radius: 0 4px 4px 0;
313
  cursor: pointer;
 
 
314
  }
315
 
316
  .state {
 
324
 
325
  .results {
326
  margin-top: 10px;
 
 
 
327
  }
328
 
329
  .results a {
330
  display: block;
331
+ padding: 5px 0;
332
+ color: #1f77b4;
333
  text-decoration: none;
 
 
334
  }
335
 
336
  .results a:hover {
337
+ text-decoration: underline;
338
  }
339
 
340
  .select-wrapper {
 
403
  }
404
 
405
  #colorLegend {
406
+ display: block !important;
407
+ visibility: visible !important;
408
+ opacity: 1 !important;
409
  margin-top: 10px;
410
  }
411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  #zoom {
413
  position: absolute;
414
  bottom: 40px;
 
452
  }
453
 
454
  #key-features {
455
+ margin-bottom: 15px;
456
  }
457
 
458
  #key-features ul {
459
+ padding-left: 20px;
 
 
 
 
 
460
  }
461
 
462
  #node-info {
 
469
  z-index: 10;
470
  display: none;
471
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
472
+ }
473
+
474
+ .search-wrapper {
475
+ display: flex;
476
+ }
477
+
478
+ #search-input {
479
+ flex: 1;
480
+ padding: 6px 10px;
481
+ border: 1px solid #ccc;
482
+ border-radius: 4px 0 0 4px;
483
+ }
484
+
485
+ #search-button {
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: center;
489
+ width: 30px;
490
+ background-color: #1f77b4;
491
+ color: white;
492
+ border-radius: 0 4px 4px 0;
493
+ cursor: pointer;
494
+ }
495
+
496
+ .col {
497
+ margin-bottom: 20px;
498
  }
daily-paper-atlas/js/main.js ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Force edge colors to match their source nodes
2
+ function forceEdgeColors() {
3
+ console.log("Forcibly updating all edge colors to match their source nodes");
4
+
5
+ // Create a map of node IDs to colors for faster lookup
6
+ let nodeColors = {};
7
+ sigmaInstance.iterNodes(function(node) {
8
+ nodeColors[node.id] = node.color || '#aaa';
9
+ });
10
+
11
+ // Update all edge colors based on their source nodes
12
+ sigmaInstance.iterEdges(function(edge) {
13
+ const sourceColor = nodeColors[edge.source];
14
+ if (sourceColor) {
15
+ edge.color = sourceColor;
16
+ console.log(`Updated edge ${edge.id} color to ${sourceColor} from source ${edge.source}`);
17
+ } else {
18
+ console.log(`Could not find color for edge ${edge.id}'s source node ${edge.source}`);
19
+ }
20
+ });
21
+
22
+ sigmaInstance.refresh();
23
+ console.log("Edge colors have been forcibly updated");
24
+ }
25
+
26
+ // Initialize the graph with the loaded data
27
+ function initializeGraph(data) {
28
+ graph = data;
29
+ console.log("Initializing graph with nodes:", graph.nodes.length, "edges:", graph.edges.length);
30
+
31
+ try {
32
+ // Initialize Sigma instance using the older sigma.init pattern
33
+ sigmaInstance = sigma.init(document.getElementById('sigma-canvas'));
34
+
35
+ // Configure mouse properties to ensure events work
36
+ sigmaInstance.mouseProperties({
37
+ maxRatio: 32,
38
+ minRatio: 0.5,
39
+ mouseEnabled: true,
40
+ mouseInertia: 0.8
41
+ });
42
+
43
+ console.log("Sigma mouse properties configured");
44
+
45
+ // Add nodes and edges to sigma
46
+ for (let i = 0; i < graph.nodes.length; i++) {
47
+ let node = graph.nodes[i];
48
+ sigmaInstance.addNode(node.id, {
49
+ label: node.label || node.id,
50
+ x: node.x || Math.random() * 100,
51
+ y: node.y || Math.random() * 100,
52
+ size: node.size || 1,
53
+ color: node.color || (node.type && config.nodeTypes && config.nodeTypes[node.type] ?
54
+ config.nodeTypes[node.type].color : nodeTypes[node.type]?.color || '#666'),
55
+ type: node.type
56
+ });
57
+ }
58
+
59
+ // First add all nodes, then add edges with colors matching their source nodes
60
+ for (let i = 0; i < graph.edges.length; i++) {
61
+ let edge = graph.edges[i];
62
+
63
+ // Find source node to match its color
64
+ let sourceNodeColor = '#aaa';
65
+ sigmaInstance.iterNodes(function(node) {
66
+ if (node.id == edge.source) {
67
+ sourceNodeColor = node.color;
68
+ console.log(`Setting edge ${edge.id} color to match source node ${node.id}: ${sourceNodeColor}`);
69
+ }
70
+ });
71
+
72
+ sigmaInstance.addEdge(edge.id, edge.source, edge.target, {
73
+ size: edge.size || 1,
74
+ color: sourceNodeColor
75
+ });
76
+ }
77
+
78
+ // Configure drawing properties
79
+ sigmaInstance.drawingProperties({
80
+ labelThreshold: config.sigma?.drawingProperties?.labelThreshold || 8,
81
+ defaultLabelColor: config.sigma?.drawingProperties?.defaultLabelColor || '#000',
82
+ defaultLabelSize: config.sigma?.drawingProperties?.defaultLabelSize || 14,
83
+ defaultEdgeType: config.sigma?.drawingProperties?.defaultEdgeType || 'curve',
84
+ defaultHoverLabelBGColor: config.sigma?.drawingProperties?.defaultHoverLabelBGColor || '#002147',
85
+ defaultLabelHoverColor: config.sigma?.drawingProperties?.defaultLabelHoverColor || '#fff',
86
+ borderSize: 2,
87
+ nodeBorderColor: '#fff',
88
+ defaultNodeBorderColor: '#fff',
89
+ defaultNodeHoverColor: '#fff',
90
+ edgeColor: 'source', // This tells sigma to use source node color for edges
91
+ defaultEdgeColor: '#f00' // Set a default that's noticeable so we can see if our explicit coloring fails
92
+ });
93
+
94
+ // Configure graph properties
95
+ sigmaInstance.graphProperties({
96
+ minNodeSize: config.sigma?.graphProperties?.minNodeSize || 1,
97
+ maxNodeSize: config.sigma?.graphProperties?.maxNodeSize || 8,
98
+ minEdgeSize: config.sigma?.graphProperties?.minEdgeSize || 0.5,
99
+ maxEdgeSize: config.sigma?.graphProperties?.maxEdgeSize || 2,
100
+ sideMargin: 50
101
+ });
102
+
103
+ // Force redraw and refresh
104
+ sigmaInstance.draw(2, 2, 2, 2);
105
+
106
+ // Force edge colors one more time after drawing
107
+ forceEdgeColors();
108
+
109
+ console.log("Sigma instance created and configured:", sigmaInstance);
110
+
111
+ // Initialize node colors and sizes by type
112
+ applyNodeStyles();
113
+
114
+ // Force edge colors again after applyNodeStyles
115
+ forceEdgeColors();
116
+
117
+ // Initialize filtering
118
+ initFilters();
119
+
120
+ // Bind events
121
+ bindEvents();
122
+ } catch (e) {
123
+ console.error("Error initializing sigma instance:", e);
124
+ }
125
+ }
126
+
127
+ // Apply node styles based on node type
128
+ function applyNodeStyles() {
129
+ if (!sigmaInstance) return;
130
+ try {
131
+ // First update node colors
132
+ sigmaInstance.iterNodes(function(node) {
133
+ if (node.type && config.nodeTypes && config.nodeTypes[node.type]) {
134
+ node.color = config.nodeTypes[node.type].color;
135
+ node.size = config.nodeTypes[node.type].size;
136
+ } else if (node.type && nodeTypes[node.type]) {
137
+ node.color = nodeTypes[node.type].color;
138
+ node.size = nodeTypes[node.type].size;
139
+ }
140
+ });
141
+
142
+ // Then update edge colors to match their source nodes
143
+ sigmaInstance.iterEdges(function(edge) {
144
+ sigmaInstance.iterNodes(function(node) {
145
+ if (node.id == edge.source) {
146
+ edge.color = node.color;
147
+ }
148
+ });
149
+ });
150
+
151
+ sigmaInstance.refresh();
152
+ } catch (e) {
153
+ console.error("Error applying node styles:", e);
154
+ }
155
+ }
156
+
157
+ // Color nodes by attribute
158
+ function colorNodesByAttribute(attribute) {
159
+ if (!sigmaInstance) return;
160
+
161
+ console.log("Coloring nodes by attribute:", attribute);
162
+ // Get all unique values for the attribute
163
+ let values = {};
164
+ let valueCount = 0;
165
+
166
+ sigmaInstance.iterNodes(function(n) {
167
+ let value = n[attribute] || 'unknown';
168
+ if (!values[value]) {
169
+ values[value] = true;
170
+ valueCount++;
171
+ }
172
+ });
173
+
174
+ // Assign colors to values
175
+ let valueColors = {};
176
+ let i = 0;
177
+ let palette = config.colorPalette || colors;
178
+
179
+ for (let value in values) {
180
+ valueColors[value] = palette[i % palette.length];
181
+ i++;
182
+ }
183
+
184
+ // Update node colors
185
+ sigmaInstance.iterNodes(function(n) {
186
+ let value = n[attribute] || 'unknown';
187
+ n.originalColor = valueColors[value];
188
+ n.color = valueColors[value];
189
+ });
190
+
191
+ // Update edge colors to match their source nodes
192
+ sigmaInstance.iterEdges(function(edge) {
193
+ sigmaInstance.iterNodes(function(node) {
194
+ if (node.id == edge.source) {
195
+ edge.originalColor = node.color;
196
+ edge.color = node.color;
197
+ }
198
+ });
199
+ });
200
+
201
+ sigmaInstance.refresh();
202
+
203
+ // Update color legend
204
+ updateColorLegend(valueColors);
205
+ }
206
+
207
+ // Display node details (used when a node is clicked)
208
+ function nodeActive(nodeId) {
209
+ console.log("nodeActive called with id:", nodeId);
210
+
211
+ // Find the node
212
+ var node = null;
213
+ sigmaInstance.iterNodes(function(n) {
214
+ if (n.id == nodeId) {
215
+ node = n;
216
+ }
217
+ });
218
+
219
+ if (!node) {
220
+ console.error("Node not found:", nodeId);
221
+ return;
222
+ }
223
+
224
+ console.log("Node found:", node);
225
+ sigmaInstance.detail = true;
226
+ selectedNode = node;
227
+
228
+ // Find neighbors
229
+ var neighbors = {};
230
+ sigmaInstance.iterEdges(function(e) {
231
+ if (e.source == nodeId || e.target == nodeId) {
232
+ neighbors[e.source == nodeId ? e.target : e.source] = {
233
+ name: e.label || "",
234
+ color: e.color
235
+ };
236
+ // Keep edges connected to this node colored as they were
237
+ e.originalColor = e.color;
238
+ }
239
+ });
240
+
241
+ console.log("Neighbors found:", Object.keys(neighbors).length);
242
+
243
+ // Update node appearance
244
+ sigmaInstance.iterNodes(function(n) {
245
+ if (n.id == nodeId) {
246
+ n.originalColor = n.color;
247
+ n.size = n.size * 1.5; // Emphasize selected node
248
+ } else if (neighbors[n.id]) {
249
+ n.originalColor = n.color;
250
+ } else {
251
+ n.originalColor = n.originalColor || n.color;
252
+ n.color = greyColor;
253
+ }
254
+ });
255
+
256
+ // Update edge appearance - grey out edges not connected to this node
257
+ sigmaInstance.iterEdges(function(e) {
258
+ if (e.source == nodeId || e.target == nodeId) {
259
+ // Keep the edge's original color
260
+ e.originalColor = e.color;
261
+ } else {
262
+ e.originalColor = e.originalColor || e.color;
263
+ e.color = greyColor;
264
+ }
265
+ });
266
+
267
+ // Refresh display
268
+ sigmaInstance.refresh();
269
+
270
+ // Populate connection list
271
+ var connectionList = [];
272
+ for (var id in neighbors) {
273
+ var neighbor = null;
274
+ sigmaInstance.iterNodes(function(n) {
275
+ if (n.id == id) {
276
+ neighbor = n;
277
+ }
278
+ });
279
+
280
+ if (neighbor) {
281
+ connectionList.push('<li><a href="#" data-node-id="' + id + '">' + (neighbor.label || id) + '</a></li>');
282
+ }
283
+ }
284
+
285
+ // Show node details panel
286
+ try {
287
+ console.log("Displaying attribute pane");
288
+ // Make absolutely sure the panel is visible with both CSS approaches
289
+ $('#attributepane').show().css('display', 'block');
290
+
291
+ // Update panel content
292
+ $('.nodeattributes .name').text(node.label || node.id);
293
+
294
+ let dataHTML = '';
295
+ for (let attr in node) {
296
+ if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' &&
297
+ attr !== 'label' && attr !== 'originalColor' && attr !== 'hidden' &&
298
+ typeof node[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' &&
299
+ attr !== 'displaySize') {
300
+ dataHTML += '<div><strong>' + attr + ':</strong> ' + node[attr] + '</div>';
301
+ }
302
+ }
303
+
304
+ if (dataHTML === '') {
305
+ dataHTML = '<div>No additional attributes</div>';
306
+ }
307
+
308
+ $('.nodeattributes .data').html(dataHTML);
309
+ $('.nodeattributes .link ul').html(connectionList.length ? connectionList.join('') : '<li>No connections</li>');
310
+
311
+ // Set up click event for neighbor nodes
312
+ $('.nodeattributes .link ul li a').click(function(e) {
313
+ e.preventDefault();
314
+ var id = $(this).data('node-id');
315
+ nodeActive(id);
316
+ });
317
+
318
+ console.log("Attribute pane updated with node details");
319
+ } catch (e) {
320
+ console.error("Error updating attribute pane:", e);
321
+ }
322
+ }
323
+
324
+ // Reset display (used when clicking outside nodes or closing the panel)
325
+ function nodeNormal() {
326
+ console.log("nodeNormal called");
327
+ if (sigmaInstance) {
328
+ sigmaInstance.detail = false;
329
+ selectedNode = null;
330
+
331
+ // Reset node appearance
332
+ sigmaInstance.iterNodes(function(node) {
333
+ node.color = node.originalColor || node.color;
334
+ // Reset size to original
335
+ if (node.type && config.nodeTypes && config.nodeTypes[node.type]) {
336
+ node.size = config.nodeTypes[node.type].size;
337
+ } else if (node.type && nodeTypes[node.type]) {
338
+ node.size = nodeTypes[node.type].size;
339
+ }
340
+ });
341
+
342
+ // Reset edge appearance
343
+ sigmaInstance.iterEdges(function(edge) {
344
+ edge.color = edge.originalColor || edge.color;
345
+ });
346
+
347
+ // Hide panel and refresh display
348
+ $('#attributepane').css('display', 'none');
349
+ sigmaInstance.refresh();
350
+
351
+ // Force edge colors to ensure they're correct after reset
352
+ forceEdgeColors();
353
+ }
354
+ }
index.html CHANGED
@@ -48,15 +48,13 @@
48
  <h2>Key Features</h2>
49
  <ul>
50
  <li><strong>Search:</strong> Find papers or authors by name.</li>
51
- <li><strong>Color By:</strong> Customize node colors based on attributes.</li>
52
- <li><strong>Filter:</strong> Filter the network by node type.</li>
53
  <li><strong>Interactive Nodes:</strong> Click nodes to view details.</li>
54
  </ul>
55
  </div>
56
 
57
  <div id="legend">
58
  <h2>Legend:</h2>
59
- <div id="colorLegend"></div>
60
  </div>
61
  </div>
62
 
@@ -70,32 +68,6 @@
70
  <div class="results"></div>
71
  </div>
72
  </div>
73
-
74
- <div class="col">
75
- <div id="coloringselect">
76
- <h2>Color By:</h2>
77
- <div class="select-wrapper">
78
- <select id="color-attribute">
79
- <option value="type">type</option>
80
- <option value="year">year</option>
81
- <option value="category">category</option>
82
- </select>
83
- </div>
84
- </div>
85
- </div>
86
-
87
- <div class="col">
88
- <div id="filteroptions">
89
- <h2>Filter:</h2>
90
- <div class="select-wrapper">
91
- <select id="filter-select">
92
- <option value="all">Show All</option>
93
- <option value="papers">Papers Only</option>
94
- <option value="authors">Authors Only</option>
95
- </select>
96
- </div>
97
- </div>
98
- </div>
99
  </div>
100
 
101
  <div id="attributepane">
 
48
  <h2>Key Features</h2>
49
  <ul>
50
  <li><strong>Search:</strong> Find papers or authors by name.</li>
 
 
51
  <li><strong>Interactive Nodes:</strong> Click nodes to view details.</li>
52
  </ul>
53
  </div>
54
 
55
  <div id="legend">
56
  <h2>Legend:</h2>
57
+ <div id="colorLegend" style="display: block;"></div>
58
  </div>
59
  </div>
60
 
 
68
  <div class="results"></div>
69
  </div>
70
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
 
73
  <div id="attributepane">
js/main.js CHANGED
@@ -20,7 +20,14 @@ let nodeTypes = {
20
 
21
  // Initialize when document is ready
22
  $(document).ready(function() {
23
- console.log("Document ready, initializing Daily Paper Atlas");
 
 
 
 
 
 
 
24
 
25
  // Initialize attribute pane
26
  $('#attributepane').css('display', 'none');
@@ -91,6 +98,12 @@ $(document).ready(function() {
91
  let filterValue = $(this).val();
92
  filterByNodeType(filterValue);
93
  });
 
 
 
 
 
 
94
  });
95
 
96
  // Load graph data
@@ -141,6 +154,13 @@ function initializeGraph(data) {
141
  // Initialize Sigma instance using the older sigma.init pattern
142
  sigmaInstance = sigma.init(document.getElementById('sigma-canvas'));
143
 
 
 
 
 
 
 
 
144
  // Configure mouse properties to ensure events work
145
  sigmaInstance.mouseProperties({
146
  maxRatio: 32,
@@ -151,25 +171,30 @@ function initializeGraph(data) {
151
 
152
  console.log("Sigma mouse properties configured");
153
 
154
- // Add nodes and edges to sigma
 
155
  for (let i = 0; i < graph.nodes.length; i++) {
156
  let node = graph.nodes[i];
 
 
 
157
  sigmaInstance.addNode(node.id, {
158
  label: node.label || node.id,
159
  x: node.x || Math.random() * 100,
160
  y: node.y || Math.random() * 100,
161
  size: node.size || 1,
162
- color: node.color || (node.type && config.nodeTypes && config.nodeTypes[node.type] ?
163
- config.nodeTypes[node.type].color : nodeTypes[node.type]?.color || '#666'),
164
  type: node.type
165
  });
166
  }
167
 
 
 
168
  for (let i = 0; i < graph.edges.length; i++) {
169
  let edge = graph.edges[i];
170
  sigmaInstance.addEdge(edge.id, edge.source, edge.target, {
171
  size: edge.size || 1,
172
- color: edge.color || '#aaa'
173
  });
174
  }
175
 
@@ -184,7 +209,9 @@ function initializeGraph(data) {
184
  borderSize: 2,
185
  nodeBorderColor: '#fff',
186
  defaultNodeBorderColor: '#fff',
187
- defaultNodeHoverColor: '#fff'
 
 
188
  });
189
 
190
  // Configure graph properties
@@ -192,63 +219,22 @@ function initializeGraph(data) {
192
  minNodeSize: config.sigma?.graphProperties?.minNodeSize || 1,
193
  maxNodeSize: config.sigma?.graphProperties?.maxNodeSize || 8,
194
  minEdgeSize: config.sigma?.graphProperties?.minEdgeSize || 0.5,
195
- maxEdgeSize: config.sigma?.graphProperties?.maxEdgeSize || 2,
196
- sideMargin: 50
197
  });
198
 
199
- // Force redraw and refresh
200
- sigmaInstance.draw(2, 2, 2, 2);
201
- sigmaInstance.refresh();
202
 
203
- console.log("Sigma instance created and configured:", sigmaInstance);
 
 
 
 
 
 
204
 
205
- // Initialize ForceAtlas2 layout if configured
206
- if (config.features && config.features.forceAtlas2) {
207
- console.log("Starting ForceAtlas2 layout...");
208
- sigmaInstance.startForceAtlas2();
209
-
210
- setTimeout(function() {
211
- sigmaInstance.stopForceAtlas2();
212
- console.log("ForceAtlas2 layout completed");
213
- sigmaInstance.refresh();
214
-
215
- // Initialize node colors and sizes by type
216
- applyNodeStyles();
217
-
218
- // Initialize filtering
219
- initFilters();
220
-
221
- // If a default color attribute is set, apply it
222
- if (config.features && config.features.defaultColorAttribute) {
223
- $('#color-attribute').val(config.features.defaultColorAttribute);
224
- colorNodesByAttribute(config.features.defaultColorAttribute);
225
- } else {
226
- updateColorLegend(nodeTypes);
227
- }
228
-
229
- // Bind events
230
- bindEvents();
231
- }, config.features?.forceAtlas2Time || 5000);
232
- } else {
233
- // Initialize node colors and sizes by type
234
- applyNodeStyles();
235
-
236
- // Initialize filtering
237
- initFilters();
238
-
239
- // If a default color attribute is set, apply it
240
- if (config.features && config.features.defaultColorAttribute) {
241
- $('#color-attribute').val(config.features.defaultColorAttribute);
242
- colorNodesByAttribute(config.features.defaultColorAttribute);
243
- } else {
244
- updateColorLegend(nodeTypes);
245
- }
246
-
247
- // Bind events
248
- bindEvents();
249
- }
250
  } catch (e) {
251
- console.error("Error initializing sigma instance:", e);
252
  }
253
  }
254
 
@@ -315,60 +301,52 @@ function bindEvents() {
315
  return;
316
  }
317
 
318
- console.log("Binding sigma events to instance:", sigmaInstance);
319
 
320
- // Add a direct click handler to the sigma canvas
321
- document.getElementById('sigma-canvas').addEventListener('click', function(evt) {
322
- console.log("Canvas clicked, checking if it's on a node");
323
- // The event happened on canvas, now check if it was on a node
324
- var x = evt.offsetX || evt.layerX;
325
- var y = evt.offsetY || evt.layerY;
326
-
327
- var nodeFound = false;
328
- sigmaInstance.iterNodes(function(n) {
329
- if (!nodeFound && n.displayX && n.displayY && n.displaySize) {
330
- var dx = n.displayX - x;
331
- var dy = n.displayY - y;
332
- var distance = Math.sqrt(dx * dx + dy * dy);
333
-
334
- if (distance < n.displaySize) {
335
- console.log("Node found under click:", n.id);
336
- nodeFound = true;
337
- nodeActive(n.id);
338
- }
339
- }
340
- });
341
-
342
- if (!nodeFound) {
343
- console.log("No node found under click, closing node panel");
344
- nodeNormal();
345
- }
346
- });
347
-
348
- // Still try to use sigma's events
349
  try {
350
  // When a node is clicked, display its details
351
  sigmaInstance.bind('clickNode', function(e) {
352
- var node = e.target || e.data.node || e.data;
353
- console.log("Official clickNode event received:", e);
354
- var nodeId = node.id || node;
355
- console.log("Node clicked via official event:", nodeId);
356
- nodeActive(nodeId);
 
 
 
 
 
 
 
 
 
 
357
  });
358
 
359
  // When stage is clicked, close the attribute pane
360
- sigmaInstance.bind('clickStage', function() {
361
- console.log("Official clickStage event received");
362
- nodeNormal();
 
 
 
 
 
 
 
363
  });
364
 
365
  // Highlight connected nodes on hover
366
  sigmaInstance.bind('overNode', function(e) {
367
- var node = e.target || e.data.node || e.data;
368
- var nodeId = node.id || node;
 
 
 
 
 
369
  console.log("Node hover enter:", nodeId);
370
 
371
- // First identify neighbors
372
  var neighbors = {};
373
  sigmaInstance.iterEdges(function(edge) {
374
  if (edge.source == nodeId || edge.target == nodeId) {
@@ -376,21 +354,18 @@ function bindEvents() {
376
  }
377
  });
378
 
379
- // Then update node and edge colors
380
- sigmaInstance.iterNodes(function(node) {
381
- if (node.id == nodeId || neighbors[node.id]) {
382
- node.originalColor = node.color;
383
- } else {
384
- node.originalColor = node.originalColor || node.color;
385
- node.color = greyColor;
386
  }
387
  });
388
 
389
  sigmaInstance.iterEdges(function(edge) {
390
- if (edge.source == nodeId || edge.target == nodeId) {
391
- edge.originalColor = edge.color;
392
- } else {
393
- edge.originalColor = edge.originalColor || edge.color;
394
  edge.color = greyColor;
395
  }
396
  });
@@ -399,25 +374,35 @@ function bindEvents() {
399
  });
400
 
401
  sigmaInstance.bind('outNode', function(e) {
402
- var node = e.target || e.data.node || e.data;
403
- var nodeId = node.id || node;
 
 
 
 
 
404
  console.log("Node hover leave:", nodeId);
405
 
406
- if (!sigmaInstance.detail) {
407
- sigmaInstance.iterNodes(function(n) {
408
- n.color = n.originalColor || n.color;
409
- });
410
-
411
- sigmaInstance.iterEdges(function(e) {
412
- e.color = e.originalColor || e.color;
413
- });
414
-
415
- sigmaInstance.refresh();
416
- }
 
 
 
 
 
417
  });
418
- console.log("Sigma events bound successfully");
419
  } catch (e) {
420
- console.error("Error binding sigma events:", e);
421
  }
422
  }
423
 
@@ -425,133 +410,161 @@ function bindEvents() {
425
  function nodeActive(nodeId) {
426
  console.log("nodeActive called with id:", nodeId);
427
 
428
- // Find the node
429
- var node = null;
 
 
 
 
 
430
  sigmaInstance.iterNodes(function(n) {
431
  if (n.id == nodeId) {
432
- node = n;
 
 
 
433
  }
434
  });
435
 
436
- if (!node) {
437
  console.error("Node not found:", nodeId);
438
  return;
439
  }
440
 
441
- console.log("Node found:", node);
442
  sigmaInstance.detail = true;
443
- selectedNode = node;
444
 
445
  // Find neighbors
446
  var neighbors = {};
 
 
447
  sigmaInstance.iterEdges(function(e) {
448
- if (e.source == nodeId || e.target == nodeId) {
449
- neighbors[e.source == nodeId ? e.target : e.source] = {
450
- name: e.label || "",
451
- color: e.color
452
- };
453
  }
454
  });
455
 
456
- console.log("Neighbors found:", Object.keys(neighbors).length);
457
-
458
- // Update node appearance
 
459
  sigmaInstance.iterNodes(function(n) {
460
- if (n.id == nodeId) {
461
- n.color = n.originalColor || n.color;
462
- n.size = n.size * 1.5; // Emphasize selected node
463
- } else if (neighbors[n.id]) {
464
  n.color = n.originalColor || n.color;
 
 
 
465
  } else {
466
- n.originalColor = n.originalColor || n.color;
467
  n.color = greyColor;
468
  }
469
  });
470
 
471
- // Refresh display
472
- sigmaInstance.refresh();
473
-
474
- // Populate connection list
475
- var connectionList = [];
476
- for (var id in neighbors) {
477
- var neighbor = null;
478
- sigmaInstance.iterNodes(function(n) {
479
- if (n.id == id) {
480
- neighbor = n;
481
- }
482
- });
483
-
484
- if (neighbor) {
485
- connectionList.push('<li><a href="#" data-node-id="' + id + '">' + (neighbor.label || id) + '</a></li>');
486
  }
487
- }
488
-
489
  // Show node details panel
490
  try {
491
  console.log("Displaying attribute pane");
492
- // Make absolutely sure the panel is visible with both CSS approaches
493
- $('#attributepane').show().css('display', 'block');
 
 
 
 
 
494
 
495
- // Update panel content
496
- $('.nodeattributes .name').text(node.label || node.id);
497
 
498
  let dataHTML = '';
499
- for (let attr in node) {
500
  if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' &&
501
- attr !== 'label' && attr !== 'originalColor' && attr !== 'hidden' &&
502
- typeof node[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' &&
503
- attr !== 'displaySize') {
504
- dataHTML += '<div><strong>' + attr + ':</strong> ' + node[attr] + '</div>';
505
  }
506
  }
507
 
508
- if (dataHTML === '') {
509
- dataHTML = '<div>No additional attributes</div>';
510
- }
511
-
512
  $('.nodeattributes .data').html(dataHTML);
513
- $('.nodeattributes .link ul').html(connectionList.length ? connectionList.join('') : '<li>No connections</li>');
514
 
515
- // Set up click event for neighbor nodes
 
 
 
 
 
 
 
 
 
 
 
 
516
  $('.nodeattributes .link ul li a').click(function(e) {
517
  e.preventDefault();
518
- var id = $(this).data('node-id');
519
- nodeActive(id);
520
  });
521
 
522
- console.log("Attribute pane updated with node details");
523
  } catch (e) {
524
  console.error("Error updating attribute pane:", e);
525
  }
 
 
 
526
  }
527
 
528
  // Reset display (used when clicking outside nodes or closing the panel)
529
  function nodeNormal() {
530
  console.log("nodeNormal called");
531
- if (sigmaInstance) {
532
- sigmaInstance.detail = false;
533
- selectedNode = null;
534
-
535
- // Reset node appearance
536
- sigmaInstance.iterNodes(function(node) {
537
- node.color = node.originalColor || node.color;
538
- // Reset size to original
539
- if (node.type && config.nodeTypes && config.nodeTypes[node.type]) {
540
- node.size = config.nodeTypes[node.type].size;
541
- } else if (node.type && nodeTypes[node.type]) {
542
- node.size = nodeTypes[node.type].size;
543
- }
544
- });
545
-
546
- // Reset edge appearance
547
- sigmaInstance.iterEdges(function(edge) {
548
- edge.color = edge.originalColor || edge.color;
549
- });
550
-
551
- // Hide panel and refresh display
552
- $('#attributepane').css('display', 'none');
553
- sigmaInstance.refresh();
554
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  }
556
 
557
  // Color nodes by attribute
@@ -644,4 +657,102 @@ function searchNodes(term) {
644
  let nodeId = $(this).data('node-id');
645
  nodeActive(nodeId);
646
  });
647
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  // Initialize when document is ready
22
  $(document).ready(function() {
23
+ console.log("Document ready, checking Sigma.js availability");
24
+
25
+ if (typeof sigma === 'undefined') {
26
+ console.error("Sigma.js is not loaded!");
27
+ return;
28
+ }
29
+
30
+ console.log("Sigma.js version:", sigma.version);
31
 
32
  // Initialize attribute pane
33
  $('#attributepane').css('display', 'none');
 
98
  let filterValue = $(this).val();
99
  filterByNodeType(filterValue);
100
  });
101
+
102
+ // Call updateLegend immediately to ensure it runs
103
+ setTimeout(function() {
104
+ console.log("Forcing legend update from document ready");
105
+ updateLegend();
106
+ }, 500);
107
  });
108
 
109
  // Load graph data
 
154
  // Initialize Sigma instance using the older sigma.init pattern
155
  sigmaInstance = sigma.init(document.getElementById('sigma-canvas'));
156
 
157
+ console.log("Sigma instance created:", sigmaInstance);
158
+
159
+ if (!sigmaInstance) {
160
+ console.error("Failed to create sigma instance");
161
+ return;
162
+ }
163
+
164
  // Configure mouse properties to ensure events work
165
  sigmaInstance.mouseProperties({
166
  maxRatio: 32,
 
171
 
172
  console.log("Sigma mouse properties configured");
173
 
174
+ // Add nodes to the graph
175
+ console.log("Adding nodes to sigma instance...");
176
  for (let i = 0; i < graph.nodes.length; i++) {
177
  let node = graph.nodes[i];
178
+ let nodeColor = node.color || (node.type && config.nodeTypes && config.nodeTypes[node.type] ?
179
+ config.nodeTypes[node.type].color : nodeTypes[node.type]?.color || '#666');
180
+
181
  sigmaInstance.addNode(node.id, {
182
  label: node.label || node.id,
183
  x: node.x || Math.random() * 100,
184
  y: node.y || Math.random() * 100,
185
  size: node.size || 1,
186
+ color: nodeColor,
 
187
  type: node.type
188
  });
189
  }
190
 
191
+ // Add edges to the graph
192
+ console.log("Adding edges to sigma instance...");
193
  for (let i = 0; i < graph.edges.length; i++) {
194
  let edge = graph.edges[i];
195
  sigmaInstance.addEdge(edge.id, edge.source, edge.target, {
196
  size: edge.size || 1,
197
+ color: edge.color || '#ccc'
198
  });
199
  }
200
 
 
209
  borderSize: 2,
210
  nodeBorderColor: '#fff',
211
  defaultNodeBorderColor: '#fff',
212
+ defaultNodeHoverColor: '#fff',
213
+ edgeColor: 'target',
214
+ defaultEdgeColor: '#ccc'
215
  });
216
 
217
  // Configure graph properties
 
219
  minNodeSize: config.sigma?.graphProperties?.minNodeSize || 1,
220
  maxNodeSize: config.sigma?.graphProperties?.maxNodeSize || 8,
221
  minEdgeSize: config.sigma?.graphProperties?.minEdgeSize || 0.5,
222
+ maxEdgeSize: config.sigma?.graphProperties?.maxEdgeSize || 2
 
223
  });
224
 
225
+ // Force initial rendering
226
+ sigmaInstance.draw();
 
227
 
228
+ console.log("Graph data loaded into sigma instance");
229
+
230
+ // Bind events
231
+ console.log("Binding events...");
232
+ bindEvents();
233
+
234
+ console.log("Graph initialization complete");
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  } catch (e) {
237
+ console.error("Error in initializeGraph:", e, e.stack);
238
  }
239
  }
240
 
 
301
  return;
302
  }
303
 
304
+ console.log("Starting to bind sigma events...");
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  try {
307
  // When a node is clicked, display its details
308
  sigmaInstance.bind('clickNode', function(e) {
309
+ console.log("Node clicked!", e);
310
+ if (!e || !e.data || !e.data.node) {
311
+ console.error("Click event missing node data");
312
+ return;
313
+ }
314
+
315
+ var node = e.data.node;
316
+ console.log("Clicked node:", node);
317
+
318
+ if (e.data.captor.isDragging) {
319
+ console.log("Ignoring click while dragging");
320
+ return;
321
+ }
322
+
323
+ nodeActive(node.id);
324
  });
325
 
326
  // When stage is clicked, close the attribute pane
327
+ sigmaInstance.bind('clickStage', function(e) {
328
+ console.log("Stage clicked!", e);
329
+ if (!e.data.node) {
330
+ nodeNormal();
331
+ }
332
+ });
333
+
334
+ // Add direct DOM click handler as backup
335
+ document.getElementById('sigma-canvas').addEventListener('click', function(e) {
336
+ console.log("Direct canvas click detected", e);
337
  });
338
 
339
  // Highlight connected nodes on hover
340
  sigmaInstance.bind('overNode', function(e) {
341
+ // --- Completely disable hover effects when a node is selected ---
342
+ if (sigmaInstance.detail) {
343
+ return;
344
+ }
345
+
346
+ var node = e.data.node;
347
+ var nodeId = node.id;
348
  console.log("Node hover enter:", nodeId);
349
 
 
350
  var neighbors = {};
351
  sigmaInstance.iterEdges(function(edge) {
352
  if (edge.source == nodeId || edge.target == nodeId) {
 
354
  }
355
  });
356
 
357
+ sigmaInstance.iterNodes(function(n) {
358
+ // Store original color only if not already stored
359
+ if (n.originalColor === undefined) n.originalColor = n.color;
360
+ if (n.id != nodeId && !neighbors[n.id]) {
361
+ n.color = greyColor;
 
 
362
  }
363
  });
364
 
365
  sigmaInstance.iterEdges(function(edge) {
366
+ // Store original color only if not already stored
367
+ if (edge.originalColor === undefined) edge.originalColor = edge.color;
368
+ if (edge.source != nodeId && edge.target != nodeId) {
 
369
  edge.color = greyColor;
370
  }
371
  });
 
374
  });
375
 
376
  sigmaInstance.bind('outNode', function(e) {
377
+ // --- Completely disable hover effects when a node is selected ---
378
+ if (sigmaInstance.detail) {
379
+ return;
380
+ }
381
+
382
+ var node = e.data.node;
383
+ var nodeId = node.id;
384
  console.log("Node hover leave:", nodeId);
385
 
386
+ // Restore original colors and clean up
387
+ sigmaInstance.iterNodes(function(n) {
388
+ if (n.originalColor !== undefined) {
389
+ n.color = n.originalColor;
390
+ delete n.originalColor;
391
+ }
392
+ });
393
+
394
+ sigmaInstance.iterEdges(function(e_edge) {
395
+ if (e_edge.originalColor !== undefined) {
396
+ e_edge.color = e_edge.originalColor;
397
+ delete e_edge.originalColor;
398
+ }
399
+ });
400
+
401
+ sigmaInstance.refresh();
402
  });
403
+ console.log("Event binding completed successfully");
404
  } catch (e) {
405
+ console.error("Error in bindEvents:", e);
406
  }
407
  }
408
 
 
410
  function nodeActive(nodeId) {
411
  console.log("nodeActive called with id:", nodeId);
412
 
413
+ if (!sigmaInstance) {
414
+ console.error("Sigma instance not ready for nodeActive");
415
+ return;
416
+ }
417
+
418
+ // Find the selected node
419
+ var selected = null;
420
  sigmaInstance.iterNodes(function(n) {
421
  if (n.id == nodeId) {
422
+ selected = n;
423
+ console.log("Found selected node:", n);
424
+ // Store original size if not already stored
425
+ if (n.originalSize === undefined) n.originalSize = n.size;
426
  }
427
  });
428
 
429
+ if (!selected) {
430
  console.error("Node not found:", nodeId);
431
  return;
432
  }
433
 
434
+ console.log("Node found:", selected);
435
  sigmaInstance.detail = true;
436
+ selectedNode = selected;
437
 
438
  // Find neighbors
439
  var neighbors = {};
440
+ neighbors[nodeId] = true; // Include the selected node itself
441
+
442
  sigmaInstance.iterEdges(function(e) {
443
+ if (e.source == nodeId) {
444
+ neighbors[e.target] = true;
445
+ } else if (e.target == nodeId) {
446
+ neighbors[e.source] = true;
 
447
  }
448
  });
449
 
450
+ var neighborIds = Object.keys(neighbors);
451
+ console.log("Neighbors found (including self):", neighborIds.length);
452
+
453
+ // Dim non-neighbor nodes and edges
454
  sigmaInstance.iterNodes(function(n) {
455
+ if (neighbors[n.id]) {
 
 
 
456
  n.color = n.originalColor || n.color;
457
+ if (n.id === nodeId) {
458
+ n.size = (n.originalSize || n.size) * 1.5;
459
+ }
460
  } else {
 
461
  n.color = greyColor;
462
  }
463
  });
464
 
465
+ sigmaInstance.iterEdges(function(e) {
466
+ if (neighbors[e.source] && neighbors[e.target]) {
467
+ e.color = e.originalColor || e.color;
468
+ } else {
469
+ e.color = greyColor;
 
 
 
 
 
 
 
 
 
 
470
  }
471
+ });
472
+
473
  // Show node details panel
474
  try {
475
  console.log("Displaying attribute pane");
476
+ $('#attributepane')
477
+ .show()
478
+ .css({
479
+ 'display': 'block',
480
+ 'visibility': 'visible',
481
+ 'opacity': '1'
482
+ });
483
 
484
+ $('.nodeattributes .name').text(selected.label || selected.id);
 
485
 
486
  let dataHTML = '';
487
+ for (let attr in selected) {
488
  if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' &&
489
+ attr !== 'label' && attr !== 'originalColor' && attr !== 'originalSize' && attr !== 'hidden' &&
490
+ typeof selected[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' &&
491
+ attr !== 'displaySize' && !attr.startsWith('_')) {
492
+ dataHTML += '<div><strong>' + attr + ':</strong> ' + selected[attr] + '</div>';
493
  }
494
  }
495
 
496
+ if (dataHTML === '') dataHTML = '<div>No additional attributes</div>';
 
 
 
497
  $('.nodeattributes .data').html(dataHTML);
 
498
 
499
+ // Build connection list
500
+ var connectionList = [];
501
+ sigmaInstance.iterNodes(function(n) {
502
+ if (neighbors[n.id] && n.id !== nodeId) {
503
+ connectionList.push('<li><a href="#" data-node-id="' + n.id + '">' + (n.label || n.id) + '</a></li>');
504
+ }
505
+ });
506
+
507
+ $('.nodeattributes .link ul')
508
+ .html(connectionList.length ? connectionList.join('') : '<li>No connections</li>')
509
+ .css('display', 'block');
510
+
511
+ // Bind click events for neighbor links
512
  $('.nodeattributes .link ul li a').click(function(e) {
513
  e.preventDefault();
514
+ var nextNodeId = $(this).data('node-id');
515
+ nodeActive(nextNodeId);
516
  });
517
 
518
+ console.log("Attribute pane updated successfully");
519
  } catch (e) {
520
  console.error("Error updating attribute pane:", e);
521
  }
522
+
523
+ // Force a refresh to show changes
524
+ sigmaInstance.refresh();
525
  }
526
 
527
  // Reset display (used when clicking outside nodes or closing the panel)
528
  function nodeNormal() {
529
  console.log("nodeNormal called");
530
+ if (!sigmaInstance) {
531
+ console.warn("Sigma instance not ready for nodeNormal");
532
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
  }
534
+
535
+ sigmaInstance.detail = false;
536
+
537
+ // Restore all nodes and edges to original state
538
+ sigmaInstance.iterNodes(function(n) {
539
+ if (n.originalColor !== undefined) {
540
+ n.color = n.originalColor;
541
+ delete n.originalColor;
542
+ }
543
+ if (n.originalSize !== undefined) {
544
+ n.size = n.originalSize;
545
+ delete n.originalSize;
546
+ }
547
+ });
548
+
549
+ sigmaInstance.iterEdges(function(e) {
550
+ if (e.originalColor !== undefined) {
551
+ e.color = e.originalColor;
552
+ delete e.originalColor;
553
+ }
554
+ });
555
+
556
+ // Reset selected node
557
+ selectedNode = null;
558
+
559
+ // Hide attribute pane
560
+ $('#attributepane').css({
561
+ 'display': 'none',
562
+ 'visibility': 'hidden'
563
+ });
564
+
565
+ // Refresh display
566
+ sigmaInstance.refresh();
567
+ console.log("Graph reset to normal state");
568
  }
569
 
570
  // Color nodes by attribute
 
657
  let nodeId = $(this).data('node-id');
658
  nodeActive(nodeId);
659
  });
660
+ }
661
+
662
+ // Update the legend with node type information
663
+ function updateLegend() {
664
+ console.log("Updating legend with node types");
665
+
666
+ // Use configured node types with fallback to default types
667
+ let typesToShow = config.nodeTypes || nodeTypes;
668
+ console.log("Node types for legend:", JSON.stringify(typesToShow));
669
+
670
+ // If typesToShow is empty or has no properties, use a default set
671
+ if (!typesToShow || Object.keys(typesToShow).length === 0) {
672
+ console.log("No node types found, using defaults");
673
+ typesToShow = {
674
+ 'paper': { color: '#2ca02c', size: 3 },
675
+ 'author': { color: '#9467bd', size: 5 },
676
+ 'organization': { color: '#1f77b4', size: 4 },
677
+ 'document': { color: '#ff7f0e', size: 3 }
678
+ };
679
+ }
680
+
681
+ // Create the HTML for the legend
682
+ let legendHTML = '';
683
+
684
+ // Make sure we're iterating through the object properties properly
685
+ for (let type in typesToShow) {
686
+ if (typesToShow.hasOwnProperty(type)) {
687
+ let typeConfig = typesToShow[type];
688
+ let color = typeConfig.color || '#ccc';
689
+ console.log(`Adding legend item for ${type} with color ${color}`);
690
+
691
+ legendHTML += `<div class="legend-item">
692
+ <div class="legend-color" style="background-color: ${color};"></div>
693
+ <div class="legend-label">${type}</div>
694
+ </div>`;
695
+ }
696
+ }
697
+
698
+ // If we still have no legend items, add some defaults
699
+ if (legendHTML === '') {
700
+ console.log("Legend is still empty, adding hardcoded defaults");
701
+ legendHTML = `
702
+ <div class="legend-item">
703
+ <div class="legend-color" style="background-color: #2ca02c;"></div>
704
+ <div class="legend-label">Paper</div>
705
+ </div>
706
+ <div class="legend-item">
707
+ <div class="legend-color" style="background-color: #9467bd;"></div>
708
+ <div class="legend-label">Author</div>
709
+ </div>
710
+ <div class="legend-item">
711
+ <div class="legend-color" style="background-color: #1f77b4;"></div>
712
+ <div class="legend-label">Organization</div>
713
+ </div>
714
+ `;
715
+ }
716
+
717
+ // Add legend for edges
718
+ legendHTML += `<div class="legend-item">
719
+ <div class="legend-line"></div>
720
+ <div class="legend-label">Connections</div>
721
+ </div>`;
722
+
723
+ // Set the HTML and make sure the element exists
724
+ let legendElement = document.getElementById('colorLegend');
725
+ if (legendElement) {
726
+ console.log("Legend element found, setting HTML:", legendHTML);
727
+ legendElement.innerHTML = legendHTML;
728
+
729
+ // Force legend to be visible
730
+ legendElement.style.display = "block";
731
+
732
+ // Also try with jQuery to ensure it's visible
733
+ $('#colorLegend').html(legendHTML).show();
734
+ } else {
735
+ console.error("Legend element #colorLegend not found in the DOM");
736
+
737
+ // Try using jQuery as a fallback
738
+ console.log("Trying to find legend with jQuery");
739
+ if ($('#colorLegend').length) {
740
+ console.log("Found with jQuery, setting content");
741
+ $('#colorLegend').html(legendHTML).show();
742
+ } else {
743
+ console.error("Legend not found with jQuery either");
744
+ }
745
+ }
746
+ }
747
+
748
+ // Add a function to manually check and ensure our legend gets populated
749
+ $(window).on('load', function() {
750
+ setTimeout(function() {
751
+ console.log("Window loaded, checking if legend is populated");
752
+ let legendElement = document.getElementById('colorLegend');
753
+ if (legendElement && (!legendElement.innerHTML || legendElement.innerHTML.trim() === '')) {
754
+ console.log("Legend is empty, manually updating it");
755
+ updateLegend();
756
+ }
757
+ }, 1000);
758
+ });