diff --git a/Output/graph/cn.js b/Output/graph/cn.js index 452a8a203670c30a0d37b4e14286dd1aa7410f8f..486aab6993dee85b922b9304b92b3e3b69d18d46 100644 --- a/Output/graph/cn.js +++ b/Output/graph/cn.js @@ -1,7 +1,7 @@ /** * creates a new zoom behavior */ -var zoom = d3.zoom().on("zoom", zoomHandler); +var zoom = d3.zoom().on("zoom", handle_zoom); /** * creates svg object and associated attributes @@ -11,25 +11,26 @@ var svg = d3.select("svg.graph") .call(zoom), width = svg.attr("width"), height = svg.attr("height"); -var textinfo=''; -var textabstract=''; +var text_info=''; +var text_abstract=''; var perc; /** -* creates node object and associated attributes +* creates node object and radius */ var node, r=10, + +/** +* scale functions that return y coordinate/color of node depending on group +*/ color = d3.scaleOrdinal() .domain(["height", "input", "depth"]) .range([' #01d7c0', ' #8b90fe ', ' #a15eb2 ']), -yscale = d3.scaleOrdinal() +y_scale = d3.scaleOrdinal() .domain(["height", "input", "depth"]) .range([0, 200, 400]), -linetype=d3.scaleOrdinal() - .domain(["line","dotted"]) - .range([("8,0"),("8,8")]), -toRemove; +to_remove; /** * creates link object @@ -46,19 +47,22 @@ var rect = svg.append("rect") .attr("height", height) .attr("width", width) .style("fill", 'white') - .on('click', clickRect); + .on('click', click_rect); /** * creates svg object (legend) and associated attributes */ -var svglegend = d3.select("svg.legendsvg"), -legendposition = [65,95,125], -arrowlegendposition = [0,25], -arrowgroupnames = ["citation","self-citation"], -groupnames = ["cited by","input","reference"]; +var svg_legend = d3.select("svg.legendsvg"), +legend_position = [65,95,125], +arrow_legend_position = [0,25], +arrow_group_names = ["citation","self-citation"], +group_names = ["cited by","input","reference"], +line_type=d3.scaleOrdinal() + .domain(["line","dotted"]) + .range([("8,0"),("8,8")]); -var legend = svglegend.selectAll(".legend") - .data(legendposition) +var legend = svg_legend.selectAll(".legend") + .data(legend_position) .enter() .append("g") .attr("class","legend") @@ -69,37 +73,37 @@ legend.append("text") .attr("y", 0) .attr("dy", ".35em") .style("text-anchor", "start") - .text(function(d,i){return groupnames[i]}); + .text(function(d,i){return group_names[i]}); legend.append("circle") .attr("r", r) .attr("cx",30-r) .style("fill", color); -var legendarrow = svglegend.selectAll(".legendarr") - .data(arrowlegendposition) +var legend_arrow = svg_legend.selectAll(".legendarr") + .data(arrow_legend_position) .enter() .append("g") .attr("class","legendarr") .attr("transform", function(d) { return "translate(0," + d + ")"; }); -legendarrow.append("line") +legend_arrow.append("line") .attr("x1", 10) .attr("x2", 50) .attr("y1", 10) .attr("y2", 10) - .style("stroke-dasharray",linetype) + .style("stroke-dasharray",line_type) .style("stroke", '#999') .style("stroke-width", "1px") .style('pointer-events', 'none') - .attr('marker-end',updateMarker('#999',this)); + .attr('marker-end',update_marker('#999',this)); -legendarrow.append("text") +legend_arrow.append("text") .attr("x", 80) .attr("y", 10) .attr("dy", ".35em") .style("text-anchor", "start") - .text(function(d,i){return arrowgroupnames[i]}); + .text(function(d,i){return arrow_group_names[i]}); /** * creates a new simulation @@ -120,9 +124,9 @@ var simulation = d3.forceSimulation() .force("yscale", d3.forceY().strength(function(d) { if (d.group == "input") {return 1000;} else {return 50;} - }).y(function(d) {return yscale(d.group)})) + }).y(function(d) {return y_scale(d.group)})) .alpha(0.005) - .on("end", zoomTo); + .on("end", zoom_to); /** * creates group element @@ -139,29 +143,29 @@ d3.json("json_text.json").then(function(graph) { /** * calls update functions for links and nodes -* adds the nodes and links to the simulation +* adds the nodes, links and tick functionailty to the simulation * @param {object} nodes - nodes * @param {object} links - links */ function update(links, nodes) { - updateLinks(links); - updateNodes(nodes); + update_links(links); + update_nodes(nodes); simulation .nodes(nodes) - .on("tick", tickHandler); + .on("tick", handle_tick); simulation.force("link") .links(links); - link.attr('marker-end', function(d) {return updateMarker("#999", d.target);}) - .style("stroke-dasharray",function(d){return self_cit(d.source,d.target)? ("8,8"): ("1,0")}); + link.attr('marker-end', function(d) {return update_marker("#999", d.target);}) + .style("stroke-dasharray",function(d){return self_citation(d.source,d.target)? ("8,8"): ("1,0")}); } /** * initializes and shows links * @param {object} links - links */ -function updateLinks(links) { +function update_links(links) { link = g.append("g") .selectAll(".link") .data(links) @@ -178,14 +182,14 @@ function updateLinks(links) { * creates a click functionality of the circles and texts * @param {object} nodes - nodes */ -function updateNodes(nodes) { +function update_nodes(nodes) { node = g.selectAll(".node") .data(nodes) .enter() .append("g") .attr("class", "node") .call(d3.drag() - .on("start", dragstarted) + .on("start", start_drag) .on("drag", dragged) ); @@ -193,25 +197,25 @@ function updateNodes(nodes) { .attr("class", "circle") .attr("r", function(d) {return 1.5*r+d.citations*0.05}) .style("fill", function(d){ return color(d.group)}) - .on('click', clickNode); + .on('click', click_node); node.append("text") .attr("class", "text") .style("font-size", "15px") .style('pointer-events', 'auto') - .text(function (d) {const firstauthor=d.author[0].split(" ") - return firstauthor[firstauthor.length-1];}) - .on('click', clickNode); + .text(function (d) {const first_author=d.author[0].split(" ") + return first_author[first_author.length-1];}) + .on('click', click_node); } /** * creates arrowhead and returns its url * @param {string} color - color of arrowhead -* @param {string} target - target-node +* @param {string} target - target node */ -function updateMarker(color, target) { +function update_marker(color, target) { var radius=1.5*r+target.citations*0.05 - svg.append('defs').append('marker')//arrowhead + svg.append('defs').append('marker') .attr('id',color.replace("#", "")+radius) .attr('viewBox','-0 -5 10 10') .attr('refX',radius+9.5) @@ -222,52 +226,51 @@ function updateMarker(color, target) { .attr('xoverflow','visible') .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', color)//arrowhead color + .attr('fill', color) .style('stroke','none'); return "url(" + color + radius + ")"; }; /** -* colors the circle and its links black and removes the previous markings +* sets color of circle and its links to black and removes the previous highlights +* displays overview info of node in textbox * @param {object} node - node */ -function clickNode(node) { +function click_node(node) { d3.select(this.parentNode).raise(); fix_nodes(node); - if(toRemove){ - d3.select(toRemove).selectAll(".circle").style("stroke","none") + if(to_remove){ + d3.select(to_remove).selectAll(".circle").style("stroke","none") } - toRemove = this.parentNode; + to_remove = this.parentNode; d3.select(this.parentNode).selectAll(".circle").style("stroke","black") - marklink(node) - textfunc(node) - resetbuttonhighlight() - highlightbutton("overview") + mark_link(node) + textbox_content(node) + reset_button_highlight() + highlight_button("overview") } /** -* removes the markings of the circles and their links +* removes the highlights of the circles and their links */ -function clickRect() { +function click_rect() { fix_nodes(node); d3.selectAll(".circle").style("stroke", "none") d3.selectAll(".link") .style("stroke", "#999") - .attr('marker-end', function(d) {return updateMarker('#999', d.target);}) - textabstract=''; - textinfo=''; - resetbuttonhighlight() + .attr('marker-end', function(d) {return update_marker('#999', d.target);}) + text_abstract=''; + text_info=''; + reset_button_highlight() document.getElementById('textbox').innerHTML = "Click node"; } -function create_author_array(authors){ - authorarray = authors.split(",") - authorarray = authorarray.map(elem =>{return elem.trim();}) - - return authorarray -} - -function self_cit(source,target){ +/** +* returns true if journals have a common author (self-citation) +* @param {object} source - node +* @param {object} target - node +*/ +function self_citation(source,target){ return source.author.some(item=>target.author.includes(item)) } @@ -276,12 +279,12 @@ function self_cit(source,target){ * and to grey otherwise * @param {object} node - node */ -function marklink(node){ +function mark_link(node){ d3.selectAll(".link") .style("stroke", function(o) { - return isLinkForNode(node, o) ? "black" : "#999";}) + return is_link_for_node(node, o) ? "black" : "#999";}) .attr('marker-end', function(o) { - return isLinkForNode(node, o) ? updateMarker('#000000', o.target) : updateMarker('#999', o.target);}) + return is_link_for_node(node, o) ? update_marker('#000000', o.target) : update_marker('#999', o.target);}) } /** @@ -289,43 +292,55 @@ function marklink(node){ * @param {object} node - node * @param {object} link - link */ -function isLinkForNode(node, link){ +function is_link_for_node(node, link){ return link.source.index == node.index || link.target.index == node.index; } /** +* saves text for overview and abstract of node * outputs node info to textbox -* @param {object} node - data of current node +* @param {object} node - node */ -function textfunc(node) { - textinfo="Title:" + '</br>' + node.name + +function textbox_content(node) { + text_info="Title:" + '</br>' + node.name + '</br>' +'</br>'+"Author:"+ '</br>' +node.author+'</br>'+'</br>'+"Date:"+'</br>' - +node.year+'</br>'+'</br>'+"doi:"+'</br>'+'<a href="'+node.doi+ '">'+node.doi + +node.year+'</br>'+'</br>'+"Journal:"+'</br>'+node.journal+'</br>'+'</br>'+"doi:"+'</br>'+'<a href="'+node.doi+ '">'+node.doi +'</a>'+'</br>'+'</br>'+"Citations:"+'</br>'+node.citations; - textabstract=node.abstract; - document.getElementById('textbox').innerHTML = textinfo; + text_abstract=node.abstract; + document.getElementById('textbox').innerHTML = text_info; } -function highlightbutton(btn) { - resetbuttonhighlight(); +/** +* sets color of btn to dark gray +* @param {object} btn - button +*/ +function highlight_button(btn) { + reset_button_highlight(); document.getElementById(btn).style.background="#CACACA"; } -function resetbuttonhighlight() { +/** +* sets color of all buttons to default light gray +*/ +function reset_button_highlight() { document.getElementById("overview").style.background=''; document.getElementById("abstract").style.background=''; } -function displayabstract(a){ - if (textabstract=='' && textinfo=='') { +/** +* displays abstract in textbox if a is true, overview text otherwise +* @param {bool} a- bool +*/ +function display_abstract(a){ + if (text_abstract=='' && text_info=='') { document.getElementById('textbox').innerHTML="Click node"; } else { if (a==true) { - document.getElementById('textbox').innerHTML =textabstract; + document.getElementById('textbox').innerHTML =text_abstract; } else { - document.getElementById('textbox').innerHTML =textinfo; + document.getElementById('textbox').innerHTML =text_info; } } } @@ -333,7 +348,7 @@ function displayabstract(a){ /** * updates the positions of the links and nodes */ -function tickHandler() { +function handle_tick() { link.attr("x1", function (d) {return d.source.x;}) .attr("y1", function (d) {return d.source.y;}) .attr("x2", function (d) {return d.target.x;}) @@ -343,9 +358,9 @@ function tickHandler() { /** * initializes the dragging of the node -* @param {object} node - data of current node +* @param {object} node - node */ -function dragstarted(node) { +function start_drag(node) { d3.select(this).raise(); if (!d3.event.active) simulation.alphaTarget(0.3).restart() @@ -356,7 +371,7 @@ function dragstarted(node) { /** * applies the dragging to the node -* @param {object} node - data of current node +* @param {object} node - node */ function dragged(node) { node.fx = d3.event.x; @@ -364,6 +379,10 @@ function dragged(node) { fix_nodes(node); } +/** +* fix positions of all nodes except for the current node +* @param {object} this_node - node +*/ function fix_nodes(this_node) { node.each(function(d) { if (this_node != d) { @@ -376,20 +395,15 @@ function fix_nodes(this_node) { /** * applies the transformation (zooming or dragging) to the g element */ -function zoomHandler() { +function handle_zoom() { d3.select('g').attr("transform", d3.event.transform); } -d3.selection.prototype.moveToFront = function() { - return this.each(function(){ - this.parentNode.appendChild(this); - }); - }; /** * transforms svg so that the zoom is adapted to the size of the graph */ -function zoomTo() { +function zoom_to() { node_bounds = d3.selectAll("svg.graph").node().getBBox(); svg_bounds = d3.select("rect").node().getBBox(); @@ -403,9 +417,9 @@ function zoomTo() { /** -* transforms svg so that the zoom is reset +* transforms svg so that the zoom and drag is reset */ -function resetView() { +function reset_view() { d3.select('svg') .call(zoom.scaleTo, 1) d3.select('svg') @@ -416,28 +430,26 @@ function resetView() { } /** -* transforms svg so that it is centered +* save svg as png */ -function center() { - d3.select('svg') - .call(zoom.translateTo, 0.5 * width, 0.5 * height); -} - -function savesvg(){ - var svgString = getSVGString(svg.node()); - svgString2Image( svgString, 2*width, 2*height, 'png', save ); // passes Blob and filesize String to the callback +function save_svg(){ + var svgString = get_svg_string(svg.node()); + svg_string_to_image( svgString, 2*width, 2*height, 'png', save ); // passes Blob and filesize String to the callback function save( dataBlob, filesize ){ saveAs( dataBlob, 'D3 vis exported to PNG.png' ); // FileSaver.js function } }; -// Below are the functions that handle actual exporting: -// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback ) -function getSVGString( svgNode ) { + +/** +* generate svgString +* @param {object} svgNode - node +*/ +function get_svg_string( svgNode ) { svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink'); - var cssStyleText = getCSSStyles( svgNode ); - appendCSS( cssStyleText, svgNode ); + var cssStyleText = get_css_styles( svgNode ); + append_css( cssStyleText, svgNode ); var serializer = new XMLSerializer(); var svgString = serializer.serializeToString(svgNode); @@ -446,7 +458,7 @@ function getSVGString( svgNode ) { return svgString; - function getCSSStyles( parentElement ) { + function get_css_styles( parentElement ) { var selectorTextArr = []; // Add Parent element Id and Classes to the list @@ -496,7 +508,7 @@ function getSVGString( svgNode ) { } - function appendCSS( cssText, element ) { + function append_css( cssText, element ) { var styleElement = document.createElement("style"); styleElement.setAttribute("type","text/css"); styleElement.innerHTML = cssText; @@ -505,8 +517,15 @@ function getSVGString( svgNode ) { } } - -function svgString2Image( svgString, width, height, format, callback ) { +/** +* convert svgString to image and export it +* @param {object} svgString - svgString +* @param {object} width - width of image +* @param {object} height - height of image +* @param {object} format - format to save image in +* @param {object} callback - callback function +*/ +function svg_string_to_image( svgString, width, height, format, callback ) { var format = format ? format : 'png'; var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL diff --git a/Output/graph/index.html b/Output/graph/index.html index 5785b099a1c6fbb7be9042b775d1c6cca9222055..eabef758bae161a1b1f5a207866b4b3823ecb7da 100644 --- a/Output/graph/index.html +++ b/Output/graph/index.html @@ -2,7 +2,7 @@ <html lang="en"> <head> <meta charset="utf-8"> - + <!-- style specifications for button and div elements --> <style type="text/css"> button { width: 100px; @@ -85,20 +85,20 @@ <!-- textbox --> <div class="textbox" id = "textbox">Click node</div> - <button id="overview" class="overview" onclick='displayabstract(false), highlightbutton("overview")'>Overview</button> - <button id="abstract" class="abstract" onclick='displayabstract(true), highlightbutton("abstract")'>Abstract</button> + <button id="overview" class="overview" onclick='display_abstract(false), highlight_button("overview")'>Overview</button> + <button id="abstract" class="abstract" onclick='display_abstract(true), highlight_button("abstract")'>Abstract</button> <!-- buttons --> - <button class="reloadGraph" onclick="location.reload()">Reload Graph</button> - <button class="resetZoom" onclick="resetView()">Reset View</button> - <button class="save" onclick="savesvg()">Save</button> + <button onclick="location.reload()">Reload Graph</button> + <button class="resetZoom" onclick="reset_view()">Reset View</button> + <button class="save" onclick="save_svg()">Save</button> - <!-- link D3 (version 5) --> + <!-- D3 (version 5) --> <script src="https://d3js.org/d3.v5.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.13.0/d3-legend.js"></script> + <!-- scripts to save svg element as png --> <script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script> <script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script> - + <!-- javascript for force-directed graph --> <script type="text/javascript" id="cn" src="cn.js"></script> </body> </html> \ No newline at end of file