hesamation commited on
Commit
7c3814c
·
1 Parent(s): 675149c

clicked neighbor nodes are highlighted, working on edges

Browse files
Files changed (2) hide show
  1. config.json +1 -1
  2. js/main.js +156 -291
config.json CHANGED
@@ -23,7 +23,7 @@
23
  "hoverBehavior": "dim",
24
  "zoomButtons": true,
25
  "fancyBox": true,
26
- "forceAtlas2": true,
27
  "forceAtlas2Time": 5000,
28
  "defaultColorAttribute": "type",
29
  "labelThreshold": 8
 
23
  "hoverBehavior": "dim",
24
  "zoomButtons": true,
25
  "fancyBox": true,
26
+ "forceAtlas2": false,
27
  "forceAtlas2Time": 5000,
28
  "defaultColorAttribute": "type",
29
  "labelThreshold": 8
js/main.js CHANGED
@@ -4,7 +4,6 @@ let graph;
4
  let filter;
5
  let config = {};
6
  let greyColor = '#ccc';
7
- let activeState = { activeNodes: [], activeEdges: [] };
8
  let selectedNode = null;
9
  let colorAttributes = [];
10
  let colors = [
@@ -20,14 +19,7 @@ let nodeTypes = {
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');
@@ -87,21 +79,14 @@ $(document).ready(function() {
87
  nodeNormal();
88
  });
89
 
90
- // Set up color selector
91
- $('#color-attribute').change(function() {
92
- let attr = $(this).val();
93
- colorNodesByAttribute(attr);
94
- });
95
-
96
  // Set up filter selector
97
  $('#filter-select').change(function() {
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
  });
@@ -227,8 +212,13 @@ function initializeGraph(data) {
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");
@@ -294,119 +284,34 @@ function filterByNodeType(filterValue) {
294
  }
295
  }
296
 
297
- // Bind events
298
  function bindEvents() {
299
  if (!sigmaInstance) {
300
  console.error("Sigma instance not found when binding events");
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) {
353
- neighbors[edge.source == nodeId ? edge.target : edge.source] = true;
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
- });
372
-
373
- sigmaInstance.refresh();
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
 
409
- // Display node details (used when a node is clicked)
410
  function nodeActive(nodeId) {
411
  console.log("nodeActive called with id:", nodeId);
412
 
@@ -415,14 +320,19 @@ function nodeActive(nodeId) {
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
 
@@ -431,48 +341,87 @@ function nodeActive(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({
@@ -486,7 +435,7 @@ function nodeActive(nodeId) {
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>';
@@ -498,11 +447,16 @@ function nodeActive(nodeId) {
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>')
@@ -515,41 +469,48 @@ function nodeActive(nodeId) {
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
 
@@ -562,64 +523,33 @@ function nodeNormal() {
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
571
- function colorNodesByAttribute(attribute) {
572
- if (!sigmaInstance) return;
573
-
574
- console.log("Coloring nodes by attribute:", attribute);
575
- // Get all unique values for the attribute
576
- let values = {};
577
- let valueCount = 0;
578
-
579
- sigmaInstance.iterNodes(function(n) {
580
- let value = n[attribute] || 'unknown';
581
- if (!values[value]) {
582
- values[value] = true;
583
- valueCount++;
584
- }
585
- });
586
-
587
- // Assign colors to values
588
- let valueColors = {};
589
- let i = 0;
590
- let palette = config.colorPalette || colors;
591
-
592
- for (let value in values) {
593
- valueColors[value] = palette[i % palette.length];
594
- i++;
595
  }
596
-
597
- // Update node colors
598
- sigmaInstance.iterNodes(function(n) {
599
- let value = n[attribute] || 'unknown';
600
- n.originalColor = valueColors[value];
601
- n.color = valueColors[value];
602
- });
603
-
604
- sigmaInstance.refresh();
605
-
606
- // Update color legend
607
- updateColorLegend(valueColors);
608
- }
609
-
610
- // Update color legend
611
- function updateColorLegend(valueColors) {
612
- let legendHTML = '';
613
-
614
- for (let value in valueColors) {
615
- let color = valueColors[value];
616
- if (typeof color === 'object') {
617
- color = color.color;
618
  }
619
- legendHTML += '<div class="legenditem"><span class="legendcolor" style="background-color: ' + color + '"></span>' + value + '</div>';
620
  }
621
 
622
- $('#colorLegend').html(legendHTML);
 
623
  }
624
 
625
  // Search nodes by term
@@ -665,18 +595,6 @@ function updateLegend() {
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 = '';
@@ -686,7 +604,6 @@ function updateLegend() {
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>
@@ -695,64 +612,12 @@ function updateLegend() {
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
- });
 
4
  let filter;
5
  let config = {};
6
  let greyColor = '#ccc';
 
7
  let selectedNode = null;
8
  let colorAttributes = [];
9
  let colors = [
 
19
 
20
  // Initialize when document is ready
21
  $(document).ready(function() {
22
+ console.log("Document ready, initializing Daily Paper Atlas");
 
 
 
 
 
 
 
23
 
24
  // Initialize attribute pane
25
  $('#attributepane').css('display', 'none');
 
79
  nodeNormal();
80
  });
81
 
 
 
 
 
 
 
82
  // Set up filter selector
83
  $('#filter-select').change(function() {
84
  let filterValue = $(this).val();
85
  filterByNodeType(filterValue);
86
  });
87
 
88
+ // Call updateLegend to ensure it runs
89
  setTimeout(function() {
 
90
  updateLegend();
91
  }, 500);
92
  });
 
212
 
213
  console.log("Graph data loaded into sigma instance");
214
 
215
+ // Initialize filters
216
+ initFilters();
217
+
218
+ // Update the legend
219
+ updateLegend();
220
+
221
  // Bind events
 
222
  bindEvents();
223
 
224
  console.log("Graph initialization complete");
 
284
  }
285
  }
286
 
287
+ // Bind events based on the Model-Atlas implementation
288
  function bindEvents() {
289
  if (!sigmaInstance) {
290
  console.error("Sigma instance not found when binding events");
291
  return;
292
  }
293
 
294
+ console.log("Binding events to sigma instance");
295
 
296
+ // When a node is clicked, display its details
297
+ sigmaInstance.bind('upnodes', function(event) {
298
+ console.log("Node clicked:", event);
299
+ if (event.content && event.content.length > 0) {
300
+ var nodeId = event.content[0];
301
+ nodeActive(nodeId);
302
+ }
303
+ });
304
+
305
+ // When stage is clicked, close the attribute pane
306
+ document.getElementById('sigma-canvas').addEventListener('click', function(evt) {
307
+ // Only process if we didn't click on a node (checked by looking at sigma's settings)
308
+ if (!sigmaInstance.isMouseDown && !sigmaInstance.detail) {
309
+ nodeNormal();
310
+ }
311
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  }
313
 
314
+ // Display node details - without color changes
315
  function nodeActive(nodeId) {
316
  console.log("nodeActive called with id:", nodeId);
317
 
 
320
  return;
321
  }
322
 
323
+ if (sigmaInstance.detail && selectedNode && selectedNode.id === nodeId) {
324
+ // Already active, no need to redraw
325
+ return;
326
+ }
327
+
328
+ // Reset previous selection if any
329
+ nodeNormal();
330
+
331
  // Find the selected node
332
  var selected = null;
333
  sigmaInstance.iterNodes(function(n) {
334
  if (n.id == nodeId) {
335
  selected = n;
 
 
 
336
  }
337
  });
338
 
 
341
  return;
342
  }
343
 
344
+ // Mark as in detail view
345
  sigmaInstance.detail = true;
346
+
347
+ // Store reference to selected node
348
  selectedNode = selected;
349
 
350
  // Find neighbors
351
  var neighbors = {};
 
 
352
  sigmaInstance.iterEdges(function(e) {
353
+ if (e.source == nodeId || e.target == nodeId) {
354
+ neighbors[e.source == nodeId ? e.target : e.source] = true;
 
 
355
  }
356
  });
357
 
358
+ // In Sigma.js v0.1, we need to use a different approach for focus
359
+ // Store original colors for all nodes and edges
 
 
360
  sigmaInstance.iterNodes(function(n) {
361
+ n.attr = n.attr || {};
362
+ n.attr.originalColor = n.color;
363
+
364
+ if (n.id === nodeId) {
365
+ // Make selected node slightly larger
366
+ n.attr.originalSize = n.size;
367
+ n.size = n.size * 1.5;
368
+ } else if (!neighbors[n.id]) {
369
+ // For non-neighbor nodes, we use a custom attribute to track they should be dimmed
370
+ // (Sigma v0.1 doesn't support opacity directly)
371
+ n.attr.dimmed = true;
372
+ // Apply a transparent version of the original color
373
+ var rgb = getRGBColor(n.color);
374
+ n.color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',0.2)';
375
  }
376
  });
377
 
378
+ // Apply the same to edges
379
  sigmaInstance.iterEdges(function(e) {
380
+ e.attr = e.attr || {};
381
+
382
+ // First, store the original color
383
+ if (!e.attr.originalColor) {
384
+ e.attr.originalColor = e.color;
385
+ }
386
+
387
+ // Direct check for connected edges - need to account for both string and object references
388
+ let isConnected = false;
389
+
390
+ // Check source connection
391
+ if (typeof e.source === 'object' && e.source !== null) {
392
+ if (e.source.id == nodeId) isConnected = true;
393
+ } else if (String(e.source) == String(nodeId)) {
394
+ isConnected = true;
395
+ }
396
+
397
+ // Check target connection
398
+ if (typeof e.target === 'object' && e.target !== null) {
399
+ if (e.target.id == nodeId) isConnected = true;
400
+ } else if (String(e.target) == String(nodeId)) {
401
+ isConnected = true;
402
+ }
403
+
404
+ // For debugging
405
+ if (isConnected) {
406
+ console.log("Edge connected:", e.id,
407
+ "Source:", (typeof e.source === 'object' ? e.source.id : e.source),
408
+ "Target:", (typeof e.target === 'object' ? e.target.id : e.target));
409
+
410
+ // IMPORTANT: Make sure the color is the original (non-dimmed) color
411
+ // In Sigma.js v0.1, we need to completely reset the color property
412
+ e.color = e.attr.originalColor;
413
  } else {
414
+ // For non-connected edges, apply the dimmed color
415
+ let rgb = getRGBColor(e.attr.originalColor);
416
+ e.color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',0.2)';
417
  }
418
  });
419
+
420
+ // Force redraw
421
+ sigmaInstance.draw(2, 2, 2, 2);
422
+
423
+ // Show node details panel and populate it
424
  try {
 
425
  $('#attributepane')
426
  .show()
427
  .css({
 
435
  let dataHTML = '';
436
  for (let attr in selected) {
437
  if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' &&
438
+ attr !== 'label' && attr !== 'hidden' && attr !== 'attr' &&
439
  typeof selected[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' &&
440
  attr !== 'displaySize' && !attr.startsWith('_')) {
441
  dataHTML += '<div><strong>' + attr + ':</strong> ' + selected[attr] + '</div>';
 
447
 
448
  // Build connection list
449
  var connectionList = [];
450
+ for (var id in neighbors) {
451
+ var neighborNode = null;
452
+ sigmaInstance.iterNodes(function(n) {
453
+ if (n.id == id) neighborNode = n;
454
+ });
455
+
456
+ if (neighborNode) {
457
+ connectionList.push('<li><a href="#" data-node-id="' + id + '">' + (neighborNode.label || id) + '</a></li>');
458
  }
459
+ }
460
 
461
  $('.nodeattributes .link ul')
462
  .html(connectionList.length ? connectionList.join('') : '<li>No connections</li>')
 
469
  nodeActive(nextNodeId);
470
  });
471
 
 
472
  } catch (e) {
473
  console.error("Error updating attribute pane:", e);
474
  }
 
 
 
475
  }
476
 
477
+ // Reset display - without color changes
478
  function nodeNormal() {
479
  console.log("nodeNormal called");
480
+
481
+ if (!sigmaInstance || !sigmaInstance.detail) {
482
+ // Not in detail view, nothing to reset
483
  return;
484
  }
485
 
486
  sigmaInstance.detail = false;
487
 
488
+ // Restore all original node attributes
489
  sigmaInstance.iterNodes(function(n) {
490
+ n.attr = n.attr || {};
491
+
492
+ // Restore original color
493
+ if (n.attr.originalColor) {
494
+ n.color = n.attr.originalColor;
495
+ delete n.attr.originalColor;
496
  }
497
+
498
+ // Restore original size if it was modified
499
+ if (n.attr.originalSize) {
500
+ n.size = n.attr.originalSize;
501
+ delete n.attr.originalSize;
502
  }
503
+
504
+ // Remove dimmed flag
505
+ delete n.attr.dimmed;
506
  });
507
 
508
+ // Restore original edge colors
509
  sigmaInstance.iterEdges(function(e) {
510
+ e.attr = e.attr || {};
511
+ if (e.attr.originalColor) {
512
+ e.color = e.attr.originalColor;
513
+ delete e.attr.originalColor;
514
  }
515
  });
516
 
 
523
  'visibility': 'hidden'
524
  });
525
 
526
+ // Force redraw
527
+ sigmaInstance.draw(2, 2, 2, 2);
 
528
  }
529
 
530
+ // Helper function to convert colors to RGB
531
+ function getRGBColor(color) {
532
+ // Handle hex colors
533
+ if (color.charAt(0) === '#') {
534
+ var r = parseInt(color.substr(1, 2), 16);
535
+ var g = parseInt(color.substr(3, 2), 16);
536
+ var b = parseInt(color.substr(5, 2), 16);
537
+ return { r: r, g: g, b: b };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
538
  }
539
+ // Handle rgb colors
540
+ else if (color.startsWith('rgb')) {
541
+ var parts = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
542
+ if (parts) {
543
+ return {
544
+ r: parseInt(parts[1], 10),
545
+ g: parseInt(parts[2], 10),
546
+ b: parseInt(parts[3], 10)
547
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  }
 
549
  }
550
 
551
+ // Default fallback color
552
+ return { r: 100, g: 100, b: 100 };
553
  }
554
 
555
  // Search nodes by term
 
595
 
596
  // Use configured node types with fallback to default types
597
  let typesToShow = config.nodeTypes || nodeTypes;
 
 
 
 
 
 
 
 
 
 
 
 
598
 
599
  // Create the HTML for the legend
600
  let legendHTML = '';
 
604
  if (typesToShow.hasOwnProperty(type)) {
605
  let typeConfig = typesToShow[type];
606
  let color = typeConfig.color || '#ccc';
 
607
 
608
  legendHTML += `<div class="legend-item">
609
  <div class="legend-color" style="background-color: ${color};"></div>
 
612
  }
613
  }
614
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  // Add legend for edges
616
  legendHTML += `<div class="legend-item">
617
  <div class="legend-line"></div>
618
  <div class="legend-label">Connections</div>
619
  </div>`;
620
 
621
+ // Set the HTML
622
+ $('#colorLegend').html(legendHTML);
623
+ }