var sigInst, canvas, $GP
//Load configuration file
var config={};
var currentColorAttribute = "base_model_relation";
var currentAtlasFile = "";
//For debug allow a config=file.json parameter to specify the config
function GetQueryStringParams(sParam,defaultVal) {
var sPageURL = ""+window.location;//.search.substring(1);//This might be causing error in Safari?
if (sPageURL.indexOf("?")==-1) return defaultVal;
sPageURL=sPageURL.substr(sPageURL.indexOf("?")+1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++) {
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] == sParam) {
return sParameterName[1];
}
}
return defaultVal;
}
jQuery.getJSON(GetQueryStringParams("config","config.json"), function(data, textStatus, jqXHR) {
config=data;
currentAtlasFile = config.data;
if (config.type!="network") {
//bad config
alert("Invalid configuration settings.")
return;
}
//As soon as page is ready (and data ready) set up it
$(document).ready(setupGUI(config));
});//End JSON Config load
// FUNCTION DECLARATIONS
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
function initSigma(config) {
var data=config.data
var drawProps, graphProps,mouseProps;
if (config.sigma && config.sigma.drawingProperties)
drawProps=config.sigma.drawingProperties;
else
drawProps={
defaultLabelColor: "#000",
defaultLabelSize: 14,
defaultLabelBGColor: "#ddd",
defaultHoverLabelBGColor: "#002147",
defaultLabelHoverColor: "#fff",
labelThreshold: 10,
defaultEdgeType: "curve",
hoverFontStyle: "bold",
fontStyle: "bold",
activeFontStyle: "bold"
};
if (config.sigma && config.sigma.graphProperties)
graphProps=config.sigma.graphProperties;
else
graphProps={
minNodeSize: 1,
maxNodeSize: 7,
minEdgeSize: 0.2,
maxEdgeSize: 0.5
};
if (config.sigma && config.sigma.mouseProperties)
mouseProps=config.sigma.mouseProperties;
else
mouseProps={
minRatio: 0.75, // How far can we zoom out?
maxRatio: 20, // How far can we zoom in?
};
var a = sigma.init(document.getElementById("sigma-canvas")).drawingProperties(drawProps).graphProperties(graphProps).mouseProperties(mouseProps);
sigInst = a;
a.active = !1;
a.neighbors = {};
a.detail = !1;
dataReady = function() {
a.clusters = {};
// Initialize node clusters based on current coloring
a.iterNodes(function (b) {
a.clusters[b.color] || (a.clusters[b.color] = []);
a.clusters[b.color].push(b.id);
// Make sure colors are properly copied from the JSON to node.attr
if (!b.attr) b.attr = {};
if (b.colors && !b.attr.colors) {
b.attr.colors = b.colors;
}
});
a.bind("upnodes", function (a) {
nodeActive(a.content[0]);
});
a.draw();
configSigmaElements(config);
// Create the color legend
updateColorLegend();
// Find available color attributes from nodes
var colorAttributes = [];
var colorAttrsFound = {};
a.iterNodes(function(node) {
if (node.attr && node.attr.colors) {
for (var attr in node.attr.colors) {
if (!colorAttrsFound[attr]) {
colorAttrsFound[attr] = true;
colorAttributes.push('
');
}
}
} else if (node.colors) {
// If colors are in the original structure, copy them to attr
if (!node.attr) node.attr = {};
node.attr.colors = node.colors;
for (var attr in node.colors) {
if (!colorAttrsFound[attr]) {
colorAttrsFound[attr] = true;
colorAttributes.push('');
}
}
}
});
$GP.coloring.content(colorAttributes.join(""));
$GP.coloring.select.text(currentColorAttribute);
}
if (data.indexOf("gexf")>0 || data.indexOf("xml")>0)
a.parseGexf(data,dataReady);
else
a.parseJson(data,dataReady);
gexf = sigmaInst = null;
}
function setupGUI(config) {
// Initialise main interface elements
var logo=""; // Logo elements
if (config.logo.file) {
logo = "
";
} else if (config.logo.text) {
logo=""+config.logo.text+"
";
}
if (config.logo.link) logo=""+logo+"";
$("#maintitle").html(logo);
// #title
$("#title").html(""+config.text.title+"
");
// #titletext
$("#titletext").html(config.text.intro);
// More information
if (config.text.more) {
$("#information").html(config.text.more);
} else {
//hide more information link
$("#moreinformation").hide();
}
// Legend
// Node
if (config.legend.nodeLabel) {
$(".node").next().html(config.legend.nodeLabel);
} else {
//hide more information link
$(".node").hide();
}
// Edge
if (config.legend.edgeLabel) {
$(".edge").next().html(config.legend.edgeLabel);
} else {
//hide more information link
$(".edge").hide();
}
// Colours
if (config.legend.nodeLabel) {
$(".colours").next().html(config.legend.colorLabel);
} else {
//hide more information link
$(".colours").hide();
}
$GP = {
calculating: !1,
showgroup: !1
};
$GP.intro = $("#intro");
$GP.minifier = $GP.intro.find("#minifier");
$GP.mini = $("#minify");
$GP.info = $("#attributepane");
$GP.info_donnees = $GP.info.find(".nodeattributes");
$GP.info_name = $GP.info.find(".name");
$GP.info_link = $GP.info.find(".link");
$GP.info_data = $GP.info.find(".data");
$GP.info_close = $GP.info.find(".returntext");
$GP.info_close2 = $GP.info.find(".close");
$GP.info_p = $GP.info.find(".p");
$GP.info_close.click(nodeNormal);
$GP.info_close2.click(nodeNormal);
$GP.form = $("#mainpanel").find("form");
$GP.search = new Search($GP.form.find("#search"));
if (!config.features.search) {
$("#search").hide();
}
if (!config.features.groupSelectorAttribute) {
$("#attributeselect").hide();
}
$GP.cluster = new Cluster($GP.form.find("#attributeselect"));
$GP.coloring = new ColorSelector($GP.form.find("#coloringselect"));
$GP.atlas = new AtlasSelector($GP.form.find("#atlasselect"));
var atlases = getAvailableAtlases();
var atlasLinks = [];
for (var i = 0; i < atlases.length; i++) {
atlasLinks.push('');
}
$GP.atlas.content(atlasLinks.join(""));
$GP.atlas.select.text(atlases[0].name); // Set default atlas name
config.GP=$GP;
initSigma(config);
}
function configSigmaElements(config) {
$GP=config.GP;
// Node hover behaviour
if (config.features.hoverBehavior == "dim") {
var greyColor = '#ccc';
sigInst.bind('overnodes',function(event){
var nodes = event.content;
var neighbors = {};
sigInst.iterEdges(function(e){
if(nodes.indexOf(e.source)<0 && nodes.indexOf(e.target)<0){
if(!e.attr['grey']){
e.attr['true_color'] = e.color;
e.color = greyColor;
e.attr['grey'] = 1;
}
}else{
e.color = e.attr['grey'] ? e.attr['true_color'] : e.color;
e.attr['grey'] = 0;
neighbors[e.source] = 1;
neighbors[e.target] = 1;
}
}).iterNodes(function(n){
if(!neighbors[n.id]){
if(!n.attr['grey']){
n.attr['true_color'] = n.color;
n.color = greyColor;
n.attr['grey'] = 1;
}
}else{
n.color = n.attr['grey'] ? n.attr['true_color'] : n.color;
n.attr['grey'] = 0;
}
}).draw(2,2,2);
}).bind('outnodes',function(){
sigInst.iterEdges(function(e){
e.color = e.attr['grey'] ? e.attr['true_color'] : e.color;
e.attr['grey'] = 0;
}).iterNodes(function(n){
n.color = n.attr['grey'] ? n.attr['true_color'] : n.color;
n.attr['grey'] = 0;
}).draw(2,2,2);
});
} else if (config.features.hoverBehavior == "hide") {
sigInst.bind('overnodes',function(event){
var nodes = event.content;
var neighbors = {};
sigInst.iterEdges(function(e){
if(nodes.indexOf(e.source)>=0 || nodes.indexOf(e.target)>=0){
neighbors[e.source] = 1;
neighbors[e.target] = 1;
}
}).iterNodes(function(n){
if(!neighbors[n.id]){
n.hidden = 1;
}else{
n.hidden = 0;
}
}).draw(2,2,2);
}).bind('outnodes',function(){
sigInst.iterEdges(function(e){
e.hidden = 0;
}).iterNodes(function(n){
n.hidden = 0;
}).draw(2,2,2);
});
}
$GP.bg = $(sigInst._core.domElements.bg);
$GP.bg2 = $(sigInst._core.domElements.bg2);
var a = [],
b,x=1;
for (b in sigInst.clusters) a.push('');
//a.sort();
$GP.cluster.content(a.join(""));
b = {
minWidth: 400,
maxWidth: 800,
maxHeight: 600
};// minHeight: 300,
$("a.fb").fancybox(b);
$("#zoom").find("div.z").each(function () {
var a = $(this),
b = a.attr("rel");
a.click(function () {
if (b == "center") {
sigInst.position(0,0,1).draw();
} else {
var a = sigInst._core;
sigInst.zoomTo(a.domElements.nodes.width / 2, a.domElements.nodes.height / 2, a.mousecaptor.ratio * ("in" == b ? 1.5 : 0.5));
}
})
});
$GP.mini.click(function () {
$GP.mini.hide();
$GP.intro.show();
$GP.minifier.show()
});
$GP.minifier.click(function () {
$GP.intro.hide();
$GP.minifier.hide();
$GP.mini.show()
});
$GP.intro.find("#showGroups").click(function () {
!0 == $GP.showgroup ? showGroups(!1) : showGroups(!0)
});
a = window.location.hash.substr(1);
if (0 < a.length) switch (a) {
case "Groups":
showGroups(!0);
break;
case "information":
$.fancybox.open($("#information"), b);
break;
default:
$GP.search.exactMatch = !0, $GP.search.search(a)
$GP.search.clean();
}
// $GP.coloring = new ColorSelector($GP.form.find("#coloringselect"));
}
function Search(a) {
this.input = a.find("input[name=search]");
this.state = a.find(".state");
this.results = a.find(".results");
this.exactMatch = !1;
this.lastSearch = "";
this.searching = !1;
var b = this;
this.input.focus(function () {
var a = $(this);
a.data("focus") || (a.data("focus", !0), a.removeClass("empty"));
b.clean()
});
this.input.keydown(function (a) {
if (13 == a.which) return b.state.addClass("searching"), b.search(b.input.val()), !1
});
this.state.click(function () {
var a = b.input.val();
b.searching && a == b.lastSearch ? b.close() : (b.state.addClass("searching"), b.search(a))
});
this.dom = a;
this.close = function () {
this.state.removeClass("searching");
this.results.hide();
this.searching = !1;
this.input.val("");//SAH -- let's erase string when we close
nodeNormal()
};
this.clean = function () {
this.results.empty().hide();
this.state.removeClass("searching");
this.input.val("");
};
this.search = function (a) {
var b = !1,
c = [],
b = this.exactMatch ? ("^" + a + "$").toLowerCase() : a.toLowerCase(),
g = RegExp(b);
this.exactMatch = !1;
this.searching = !0;
this.lastSearch = a;
this.results.empty();
if (2 >= a.length) this.results.html("You must search for a name with a minimum of 3 letters.");
else {
sigInst.iterNodes(function (a) {
g.test(a.label.toLowerCase()) && c.push({
id: a.id,
name: a.label
})
});
c.length ? (b = !0, nodeActive(c[0].id)) : b = showCluster(a);
a = ["Search Results: "];
if (1 < c.length) for (var d = 0, h = c.length; d < h; d++) a.push('" + c[d].name + "");
0 == c.length && !b && a.push("No results found.");
1 < a.length && this.results.html(a.join(""));
}
if(c.length!=1) this.results.show();
if(c.length==1) this.results.hide();
}
}
function Cluster(a) {
this.cluster = a;
this.display = !1;
this.list = this.cluster.find(".list");
this.list.empty();
this.select = this.cluster.find(".select");
this.select.click(function () {
$GP.cluster.toggle()
});
this.toggle = function () {
this.display ? this.hide() : this.show()
};
this.content = function (a) {
this.list.html(a);
this.list.find("a").click(function () {
var a = $(this).attr("href").substr(1);
showCluster(a)
})
};
this.hide = function () {
this.display = !1;
this.list.hide();
this.select.removeClass("close")
};
this.show = function () {
this.display = !0;
this.list.show();
this.select.addClass("close")
}
}
// Add this after the Cluster constructor
function ColorSelector(a) {
this.coloring = a;
this.display = false;
this.list = this.coloring.find(".list");
this.list.empty();
this.select = this.coloring.find(".select");
this.select.click(function () {
$GP.coloring.toggle();
});
this.toggle = function () {
this.display ? this.hide() : this.show();
};
this.content = function (a) {
this.list.html(a);
this.list.find("a").click(function () {
var a = $(this).attr("data-attr");
changeNodeColors(a);
});
};
this.hide = function () {
this.display = false;
this.list.hide();
this.select.removeClass("close");
};
this.show = function () {
this.display = true;
this.list.show();
this.select.addClass("close");
};
}
// Add this function to change node colors
function changeNodeColors(attribute) {
currentColorAttribute = attribute;
$GP.coloring.select.text(attribute);
$GP.coloring.hide();
// Reset clusters
sigInst.clusters = {};
// Update node colors based on the selected attribute
sigInst.iterNodes(function (n) {
if (n.attr && n.attr.colors && n.attr.colors[attribute]) {
n.color = n.attr.colors[attribute];
}
// Update clusters
sigInst.clusters[n.color] || (sigInst.clusters[n.color] = []);
if (sigInst.clusters[n.color].indexOf(n.id) === -1) {
sigInst.clusters[n.color].push(n.id);
}
});
// Update node colors based on the selected attribute
sigInst.iterEdges(function (e) {
if (e.attr && e.attr.colors && e.attr.colors[attribute]) {
e.color = e.attr.colors[attribute];
}
});
// Update color legend
updateColorLegend();
// Redraw the graph
sigInst.draw(2, 2, 2);
}
function updateColorLegend() {
var legendColors = {};
var legendHtml = "";
// Collect unique colors and their values
sigInst.iterNodes(function (n) {
if (n.attr && n.attr.colors && n.attr.colors[currentColorAttribute]) {
var color = n.attr.colors[currentColorAttribute];
var attrValue = "";
// Try to get the attribute value from node attributes
if (n.attr.attributes && n.attr.attributes[currentColorAttribute]) {
attrValue = n.attr.attributes[currentColorAttribute];
} else {
// If not found in attributes, use a generic label
attrValue = "Value " + Object.keys(legendColors).length;
}
if (!legendColors[color]) {
legendColors[color] = attrValue;
}
}
});
// Create legend HTML
for (var color in legendColors) {
legendHtml += '' +
legendColors[color] + '';
}
legendHtml += "";
$("#colorLegend").html(legendHtml);
}
function showGroups(a) {
a ? ($GP.intro.find("#showGroups").text("Hide groups"), $GP.bg.show(), $GP.bg2.hide(), $GP.showgroup = !0) : ($GP.intro.find("#showGroups").text("View Groups"), $GP.bg.hide(), $GP.bg2.show(), $GP.showgroup = !1)
}
function nodeNormal() {
!0 != $GP.calculating && !1 != sigInst.detail && (showGroups(!1), $GP.calculating = !0, sigInst.detail = !0, $GP.info.delay(400).animate({width:'hide'},350),$GP.cluster.hide(), sigInst.iterEdges(function (a) {
a.attr.color = !1;
a.hidden = !1
}), sigInst.iterNodes(function (a) {
a.hidden = !1;
a.attr.color = !1;
a.attr.lineWidth = !1;
a.attr.size = !1
}), sigInst.draw(2, 2, 2, 2), sigInst.neighbors = {}, sigInst.active = !1, $GP.calculating = !1, window.location.hash = "")
}
function nodeActive(a) {
var groupByDirection=false;
if (config.informationPanel.groupByEdgeDirection && config.informationPanel.groupByEdgeDirection==true) groupByDirection=true;
sigInst.neighbors = {};
sigInst.detail = !0;
var b = sigInst._core.graph.nodesIndex[a];
showGroups(!1);
var outgoing={},incoming={},mutual={};//SAH
sigInst.iterEdges(function (b) {
b.attr.lineWidth = !1;
b.hidden = !0;
n={
name: b.label,
colour: b.color
};
if (a==b.source) outgoing[b.target]=n; //SAH
else if (a==b.target) incoming[b.source]=n; //SAH
if (a == b.source || a == b.target) sigInst.neighbors[a == b.target ? b.source : b.target] = n;
b.hidden = !1, b.attr.color = "rgba(0, 0, 0, 1)";
});
var f = [];
sigInst.iterNodes(function (a) {
a.hidden = !0;
a.attr.lineWidth = !1;
a.attr.color = a.color
});
if (groupByDirection) {
//SAH - Compute intersection for mutual and remove these from incoming/outgoing
for (e in outgoing) {
//name=outgoing[e];
if (e in incoming) {
mutual[e]=outgoing[e];
delete incoming[e];
delete outgoing[e];
}
}
}
var createList=function(c) {
var f = [];
var e = [],
//c = sigInst.neighbors,
g;
for (g in c) {
var d = sigInst._core.graph.nodesIndex[g];
d.hidden = !1;
d.attr.lineWidth = !1;
d.attr.color = c[g].colour;
a != g && e.push({
id: g,
name: d.label,
group: (c[g].name)? c[g].name:"",
colour: c[g].colour
})
}
e.sort(function (a, b) {
var c = a.group.toLowerCase(),
d = b.group.toLowerCase(),
e = a.name.toLowerCase(),
f = b.name.toLowerCase();
return c != d ? c < d ? -1 : c > d ? 1 : 0 : e < f ? -1 : e > f ? 1 : 0
});
d = "";
for (g in e) {
c = e[g];
/*if (c.group != d) {
d = c.group;
f.push('' + d + "
");
}*/
f.push('' + c.name + "");
}
return f;
}
/*console.log("mutual:");
console.log(mutual);
console.log("incoming:");
console.log(incoming);
console.log("outgoing:");
console.log(outgoing);*/
var f=[];
//console.log("neighbors:");
//console.log(sigInst.neighbors);
if (groupByDirection) {
size=Object.size(mutual);
f.push("Mututal (" + size + ")
");
(size>0)? f=f.concat(createList(mutual)) : f.push("No mutual links
");
size=Object.size(incoming);
f.push("Incoming (" + size + ")
");
(size>0)? f=f.concat(createList(incoming)) : f.push("No incoming links
");
size=Object.size(outgoing);
f.push("Outgoing (" + size + ")
");
(size>0)? f=f.concat(createList(outgoing)) : f.push("No outgoing links
");
} else {
f=f.concat(createList(sigInst.neighbors));
}
//b is object of active node -- SAH
b.hidden = !1;
b.attr.color = b.color;
b.attr.lineWidth = 6;
b.attr.strokeStyle = "#000000";
sigInst.draw(2, 2, 2, 2);
$GP.info_link.find("ul").html(f.join(""));
$GP.info_link.find("li").each(function () {
var a = $(this),
b = a.attr("rel");
});
f = b.attr;
if (f.attributes) {
var image_attribute = false;
if (config.informationPanel.imageAttribute) {
image_attribute=config.informationPanel.imageAttribute;
}
e = [];
e.push('Model page: https://huggingface.co/' + f.attributes['id'] + '
')
e.push('Update model information: edit
')
temp_array = [];
g = 0;
for (var attr in f.attributes) {
var d = f.attributes[attr],
h = "";
if (attr!=image_attribute) {
h = '' + attr + ': ' + d + '
'
}
//temp_array.push(f.attributes[g].attr);
e.push(h)
}
if (image_attribute) {
//image_index = jQuery.inArray(image_attribute, temp_array);
$GP.info_name.html("
' + b.label + " ");
} else {
$GP.info_name.html("' + b.label + "
");
}
// Image field for attribute pane
$GP.info_data.html(e.join("
"))
}
$GP.info_data.show();
$GP.info_p.html("Connections:");
$GP.info.animate({width:'show'},350);
$GP.info_donnees.hide();
$GP.info_donnees.show();
sigInst.active = a;
window.location.hash = b.label;
}
function showCluster(a) {
var b = sigInst.clusters[a];
if (b && 0 < b.length) {
showGroups(!1);
sigInst.detail = !0;
b.sort();
sigInst.iterEdges(function (a) {
a.hidden = !1;
a.attr.lineWidth = !1;
a.attr.color = !1
});
sigInst.iterNodes(function (a) {
a.hidden = !0
});
for (var f = [], e = [], c = 0, g = b.length; c < g; c++) {
var d = sigInst._core.graph.nodesIndex[b[c]];
!0 == d.hidden && (e.push(b[c]), d.hidden = !1, d.attr.lineWidth = !1, d.attr.color = d.color, f.push('' + d.label + ""))
}
sigInst.clusters[a] = e;
sigInst.draw(2, 2, 2, 2);
$GP.info_name.html("" + a + "");
$GP.info_data.hide();
$GP.info_p.html("Group Members:");
$GP.info_link.find("ul").html(f.join(""));
$GP.info.animate({width:'show'},350);
$GP.search.clean();
$GP.cluster.hide();
return !0
}
return !1
}
function AtlasSelector(a) {
this.atlas = a;
this.display = false;
this.list = this.atlas.find(".list");
this.list.empty();
this.select = this.atlas.find(".select");
this.select.click(function () {
$GP.atlas.toggle();
});
this.toggle = function () {
this.display ? this.hide() : this.show();
};
this.content = function (a) {
this.list.html(a);
this.list.find("a").click(function () {
var dataFile = $(this).attr("data-file");
loadNewAtlas(dataFile);
});
};
this.hide = function () {
this.display = false;
this.list.hide();
this.select.removeClass("close");
};
this.show = function () {
this.display = true;
this.list.show();
this.select.addClass("close");
};
}
function getAvailableAtlases() {
return [
{ name: "Modalities Atlas", file: "compressed_modalities_data.json.gz" },
{ name: "Large NLP Atlas", file: "compressed_large_nlp_data.json.gz" },
];
}
function loadNewAtlas(dataFile) {
// Update the currently selected atlas in the dropdown
$GP.atlas.select.text(getAtlasNameByFile(dataFile));
$GP.atlas.hide();
config.data = dataFile;
reinitializeSigma(config);
}
function reinitializeSigma(config) {
// Remove the old sigma instance
if (sigInst) {
sigInst.kill();
}
// Recreate the sigma canvas
$(".sigma-expand").empty();
// Initialize new sigma instance
initSigma(config);
}
function getAtlasNameByFile(dataFile) {
var atlases = getAvailableAtlases();
for (var i = 0; i < atlases.length; i++) {
if (atlases[i].file === dataFile) {
return atlases[i].name;
}
}
return "Unknown Atlas";
}