// Pandora FMS - http://pandorafms.com // ================================================== // Copyright (c) 2005-2023 Pandora FMS // Please see http://pandorafms.org for full contribution list // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. /* globals jQuery, d3 */ // The recipient is the selector of the html element // The elements is an array with the names of the wheel elements // The matrix must be a 2 dimensional array with a row and a column for each element // Ex: // elements = ["a", "b", "c"]; // matrix = [[0, 0, 2], // a[a => a, a => b, a => c] // [5, 0, 1], // b[b => a, b => b, b => c] // [2, 3, 0]]; // c[c => a, c => b, c => c] function chordDiagram(recipient, elements, matrix, width, height) { d3.chart = d3.chart || {}; d3.chart.chordWheel = function(options) { // Default values var width = 700; var margin = 150; var padding = 0.02; var consoleStyle = document.getElementById("hidden-selected_style_theme") .value; var textColor = consoleStyle === "pandora_black" ? "rgb(240, 240, 240)" : "rgb(0, 0, 0)"; var tooltipColor = consoleStyle === "pandora_black" ? "rgb(0, 0, 0)" : "rgb(240, 240, 240)"; function chart(selection) { selection.each(function(data) { var matrix = data.matrix; var elements = data.elements; var radius = width / 2 - margin; // create the layout var chord = d3.layout .chord() .padding(padding) .sortSubgroups(d3.descending); // Select the svg element, if it exists. var svg = d3 .select(this) .selectAll("svg") .data([data]); // Otherwise, create the skeletal chart. var gEnter = svg .enter() .append("svg:svg") .attr("width", width) .attr("height", height) .attr("class", "dependencyWheel") .append("g") .attr( "transform", "translate(" + width / 2 + "," + height / 2 + ") scale(1.2)" ); var arc = d3.svg .arc() .innerRadius(radius) .outerRadius(radius + 20); var fill = function(d) { return "hsl(" + parseInt((d.index / 26) * 360, 10) + ",80%,70%)"; }; // Returns an event handler for fading a given chord group. var fade = function(opacity) { return function(g, i) { svg .selectAll(".chord") .filter(function(d) { return d.source.index != i && d.target.index != i; }) .transition() .style("opacity", opacity); var groups = []; svg.selectAll(".chord").filter(function(d) { if (d.source.index == i) { groups.push(d.target.index); } if (d.target.index == i) { groups.push(d.source.index); } }); groups.push(i); var length = groups.length; svg .selectAll(".group") .filter(function(d) { for (var i = 0; i < length; i++) { if (groups[i] == d.index) return false; } return true; }) .transition() .style("opacity", opacity); if (event.type == "mouseover") { const chords = chord.chords(); let aux = 0; $.each(chords, function(key, value) { if (aux < 5) { if ( (value.source.index == i && value.target.subindex == i) || (value.source.subindex == i && value.target.index == i) ) { if ( $("#tooltip").is(":hidden") || $("#tooltip").length == 0 ) { show_tooltip(value); } else { add_tooltip(value); aux++; } } } }); } else { hide_tooltip(); } }; }; chord.matrix(matrix); var rootGroup = chord.groups()[0]; var rotation = (-(rootGroup.endAngle - rootGroup.startAngle) / 2) * (180 / Math.PI); var g = gEnter .selectAll("g.group") .data(chord.groups) .enter() .append("svg:g") .attr("class", "group") .attr("transform", function(d) { return "rotate(" + rotation + ")"; }); g.append("svg:path") .style("fill", fill) .style("stroke", fill) .attr("d", arc) .on("mouseover", fade(0.1)) .on("mouseout", fade(1)) .on("mousemove", move_tooltip); g.append("svg:text") .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) .attr("style", "fill: " + textColor) .attr("transform", function(d) { return ( "rotate(" + ((d.angle * 180) / Math.PI - 90) + ")" + "translate(" + (radius + 26) + ")" + (d.angle > Math.PI ? "rotate(180)" : "") ); }) .text(function(d) { return elements[d.index]; }); gEnter .selectAll("path.chord") .data(chord.chords) .enter() .append("svg:path") .attr("class", "chord") .style("stroke", function(d) { return d3.rgb(fill(d.source)).darker(); }) .style("fill", function(d) { return fill(d.source); }) .attr("d", d3.svg.chord().radius(radius)) .attr("transform", function(d) { return "rotate(" + rotation + ")"; }) .style("opacity", 1); // Add an elaborate mouseover title for each chord. gEnter .selectAll("path.chord") .on("mouseover", over_user) .on("mouseout", out_user) .on("mousemove", move_tooltip); function move_tooltip(d) { x = d3.event.layerX + 10; y = d3.event.layerY + 10; $("#tooltip").css("left", x + "px"); $("#tooltip").css("top", y + "px"); } function over_user(d) { id = d.id; $("#" + id).css("border", "1px solid black"); $("#" + id).css("z-index", "1"); show_tooltip(d); } function out_user(d) { id = d.id; $("#" + id).css("border", ""); $("#" + id).css("z-index", ""); hide_tooltip(); } function create_tooltip(d, x, y) { if ($("#tooltip").length == 0) { $(recipient).append( $("
") .attr("id", "tooltip") .html( elements[d.source.index] + " → " + elements[d.target.index] + ": " + valueToBytes(d.source.value) + "" + "
" + elements[d.target.index] + " → " + elements[d.source.index] + ": " + valueToBytes(d.target.value) + "" ) ); } else { $("#tooltip").html( elements[d.source.index] + " → " + elements[d.target.index] + ": " + valueToBytes(d.source.value) + "" + "
" + elements[d.target.index] + " → " + elements[d.source.index] + ": " + valueToBytes(d.target.value) + "" ); } $("#tooltip").attr( "style", "background: " + tooltipColor + ";" + "color: " + textColor + ";" + "position: absolute;" + "display: inline-block;" + "width: auto;" + "max-width: 500px;" + "text-align: left;" + "padding: 10px 10px 10px 10px;" + "z-index: 2;" + "-webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "-moz-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "left: " + x + "px;" + "top: " + y + "px;" ); } function add_tooltip(d) { $("#tooltip").append( "
" + elements[d.source.index] + " → " + elements[d.target.index] + ": " + valueToBytes(d.source.value) + "" + "
" + elements[d.target.index] + " → " + elements[d.source.index] + ": " + valueToBytes(d.target.value) + "" ); } function show_tooltip(d) { x = d3.event.pageX + 10; y = d3.event.pageY + 10; create_tooltip(d, x, y); } function hide_tooltip() { $("#tooltip").hide(); } }); } chart.width = function(value) { if (!arguments.length) return width; width = value; return chart; }; chart.margin = function(value) { if (!arguments.length) return margin; margin = value; return chart; }; chart.padding = function(value) { if (!arguments.length) return padding; padding = value; return chart; }; return chart; }; var chart = d3.chart .chordWheel() .width(width) .margin(150) .padding(0.02); d3.select(recipient) .datum({ elements: elements, matrix: matrix }) .call(chart); } // The recipient is the selector of the html element // The data must be a bunch of associative arrays like this // data = { // "name": "IP Traffic", // "id": 0, // "children": [ // { // "name": "192.168.1.1", // "id": 1, // "children": [ // { // "name": "HTTP", // "id": 2, // "value": 33938 // } // ] // }, // { // "name": "192.168.1.2", // "id": 3, // "children": [ // { // "name": "HTTP", // "id": 4, // "value": 3938 // }, // { // "name": "FTP", // "id": 5, // "value": 1312 // } // ] // } // ] // }; function treeMap(recipient, data, width, height, childLinks = false) { //var isIE = BrowserDetect.browser == 'Explorer'; var isIE = true; var chartWidth = width; var chartHeight = height; const uniqueId = generateUniqueId(); if (document.getElementById("hidden-selected_style_theme") !== null) { var consoleStyle = document.getElementById("hidden-selected_style_theme") .value; } else { var consoleStyle = ""; } $("#tooltip_" + uniqueId).css( "color", consoleStyle === "pandora_black" ? "rgb(240, 240, 240)" : "rgb(0, 0, 0)" ); $("#tooltip_" + uniqueId).css( "background-color", consoleStyle === "pandora_black" ? "rgb(0, 0, 0)" : "rgb(240, 240, 240)" ); if (width === "auto") { chartWidth = $(recipient).innerWidth(); } if (height === "auto") { chartHeight = $(recipient).innerHeight(); } var xscale = d3.scale.linear().range([0, chartWidth]); var yscale = d3.scale.linear().range([0, chartHeight]); var color = d3.scale.category10(); var headerHeight = 20; var headerColor = "#555555"; var transitionDuration = 500; var root; var node; var resize_button = $(recipient) .parent() .find(".resize_button"); var treemap = d3.layout .treemap() .round(false) .size([chartWidth, chartHeight]) .sticky(true) .value(function(d) { return d.value; }); var chart = d3 .select(recipient) .append("svg:svg") .attr("width", chartWidth) .attr("height", chartHeight) .append("svg:g"); node = root = data; var nodes = treemap.nodes(root); var children = nodes.filter(function(d) { return !d.children; }); var parents = nodes.filter(function(d) { return d.children; }); // create parent cells var parentCells = chart.selectAll("g.cell.parent").data(parents, function(d) { return d.id; }); var parentEnterTransition = parentCells .enter() .append("g") .attr("class", "cell parent") .on("click", function(d) { zoom(d); resize_button.show(); }) .append("svg") .attr("class", "clip") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("height", headerHeight); parentEnterTransition .filter(function(d) { if (d.name) return d; }) .append("rect") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("height", headerHeight) .style("fill", headerColor); parentEnterTransition .filter(function(d) { if (d.name) return d; }) .append("text") .attr("class", "label") .attr("fill", "white") .attr("transform", "translate(3, 13)") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("style", "transition: all 0.5s ease-out;") .attr("height", headerHeight) .text(function(d) { return d.name; }); // update transition var parentUpdateTransition = parentCells .transition() .duration(transitionDuration); parentUpdateTransition.select(".cell").attr("transform", function(d) { return "translate(" + d.dx + "," + d.y + ")"; }); parentUpdateTransition .select("rect") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("height", headerHeight) .style("fill", headerColor); parentUpdateTransition .select(".label") .attr("transform", "translate(3, 13)") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("height", headerHeight) .text(function(d) { return d.name; }); // remove transition parentCells.exit().remove(); $(resize_button).on("click", function() { zoom(root); $(this).hide(); }); // create children cells var childrenCells = chart .selectAll("g.cell.child") .data(children, function(d) { return d.id; }); // enter transition var childEnterTransition = childrenCells .enter() .append("g") .attr("class", "cell child") .on("click", function(d) { if (childLinks) { if (node === d.parent) { window.location.href = d.link; } else { resize_button.show(); zoom(d.parent); } } else { zoom(node === d.parent ? root : d.parent); } }) .append("svg") .attr("class", "clip"); $(recipient).on("mouseover", over_user); $(recipient).on("mouseout", out_user); $(recipient).on("mousemove", move_tooltip); childEnterTransition .append("rect") .classed("background", true) .style("fill", function(d) { return d.color ? d.color : color(d.name); }); childEnterTransition .append("text") .attr("class", "label") .attr("x", function(d) { return d.dx / 2; }) .attr("y", function(d) { return d.dy / 2; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .style("display", "none") .text(function(d) { return d.name; }); // update transition var childUpdateTransition = childrenCells .transition() .duration(transitionDuration); childUpdateTransition.select(".cell").attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); childUpdateTransition .select("rect") .attr("width", function(d) { return Math.max(0.01, d.dx); }) .attr("height", function(d) { return d.dy; }); childUpdateTransition .select(".label") .attr("x", function(d) { return d.dx / 2; }) .attr("y", function(d) { return d.dy / 2; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .style("display", "none") .text(function(d) { return d.name; }); // exit transition childrenCells.exit().remove(); d3.select("select").on("change", function() { treemap.value(this.value == "size" ? size : count).nodes(root); zoom(node); }); zoom(node); function calculateSizeText() { $(recipient + " .parent .clip .label").each((key, node) => { const textElement = node; const containerWidth = parseFloat( $(textElement) .parent() .attr("width") ); const originalFontSize = 12; textElement.style.fontSize = "16px"; const computedTextLength = textElement.getComputedTextLength(); const textWidth = computedTextLength + 8; textElement.style.fontSize = originalFontSize + "px"; const scaleFactor = containerWidth / textWidth; let scaledFontSize = parseFloat(originalFontSize) * scaleFactor; scaledFontSize = scaledFontSize > 12 ? 12 : scaledFontSize; textElement.style.fontSize = scaledFontSize + "px"; }); } function size(d) { return d.size; } function count(d) { return 1; } //and another one function textHeight(d) { var ky = chartHeight / d.dy; yscale.domain([d.y, d.y + d.dy]); return (ky * d.dy) / headerHeight; } function getRGBComponents(color) { var r = color.substring(1, 3); var g = color.substring(3, 5); var b = color.substring(5, 7); return { R: parseInt(r, 16), G: parseInt(g, 16), B: parseInt(b, 16) }; } function idealTextColor(bgColor) { var nThreshold = 105; var components = getRGBComponents(bgColor); var bgDelta = components.R * 0.299 + components.G * 0.587 + components.B * 0.114; return 255 - bgDelta < nThreshold ? "#000000" : "#ffffff"; } function zoom(d) { treemap.padding([headerHeight / (chartHeight / d.dy), 0, 0, 0]).nodes(d); // moving the next two lines above treemap layout messes up padding of zoom result var kx = chartWidth / d.dx; var ky = chartHeight / d.dy; var level = d; xscale.domain([d.x, d.x + d.dx]); yscale.domain([d.y, d.y + d.dy]); if (node != level) { chart.selectAll(".cell.child .label").style("display", "none"); } var zoomTransition = chart .selectAll("g.cell") .transition() .duration(transitionDuration) .attr("transform", function(d) { return "translate(" + xscale(d.x) + "," + yscale(d.y) + ")"; }) .each("start", function() { d3.select(this) .select("label") .style("display", "none"); }) .each("end", function(d, i) { if (!i && level !== self.root) { chart .selectAll(".cell.child") .filter(function(d) { return d.parent === self.node; // only get the children for selected group }) .select(".label") .style("display", "") .style("color", function(d) { return idealTextColor(color(d.parent.name)); }); } }); zoomTransition .select(".clip") .attr("width", function(d) { return Math.max(0.01, kx * d.dx); }) .attr("height", function(d) { return d.children ? headerHeight : Math.max(0.01, ky * d.dy); }); zoomTransition .select(".label") .attr("width", function(d) { return Math.max(0.01, kx * d.dx); }) .attr("height", function(d) { return d.children ? headerHeight : Math.max(0.01, ky * d.dy); }) .text(function(d) { return d.name; }); zoomTransition .select(".child .label") .attr("x", function(d) { return (kx * d.dx) / 2; }) .attr("y", function(d) { return (ky * d.dy) / 2; }); zoomTransition .select("rect") .attr("width", function(d) { return Math.max(0.01, kx * d.dx); }) .attr("height", function(d) { return d.children ? headerHeight : Math.max(0.01, ky * d.dy); }); node = d; if (d3.event) { d3.event.stopPropagation(); } setTimeout(() => { calculateSizeText(); }, transitionDuration); } function position() { this.style("left", function(d) { return d.x + "px"; }) .style("top", function(d) { return d.y + "px"; }) .style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; }) .style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; }); } function move_tooltip(d) { x = d.offsetX + 40; y = d.offsetY + 40; $("#tooltip_" + uniqueId).css("left", x + "px"); $("#tooltip_" + uniqueId).css("top", y + "px"); } function over_user(d) { id = d.id; $("#" + id).css("border", "1px solid black"); $("#" + id).css("z-index", "1"); show_tooltip(d); } function out_user(d) { id = d.id; $("#" + id).css("border", ""); $("#" + id).css("z-index", ""); hide_tooltip(); } function create_tooltip(d, x, y) { if ($("#tooltip_" + uniqueId).length == 0) { $(recipient).append( $("
") .attr("id", "tooltip_" + uniqueId) .html(d.target.__data__.tooltip_content) ); } else { $("#tooltip_" + uniqueId).html(d.target.__data__.tooltip_content); } $("#tooltip_" + uniqueId).attr( "style", "background: #fff;" + "color: #111;" + "position: absolute;" + "display: block;" + "width: 200px;" + "text-align: left;" + "padding: 10px 10px 10px 10px;" + "z-index: 2;" + "-webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "-moz-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "left: " + x + "px;" + "top: " + y + "px;" ); } function show_tooltip(d) { x = d.offsetX + 10; y = d.offsetY + 10; if (d.target.__data__) { create_tooltip(d, x, y); } } function hide_tooltip() { $("#tooltip_" + uniqueId).hide(); } function generateUniqueId() { const timestamp = new Date().getTime(); const randomNum = Math.floor(Math.random() * 10000); const uniqueId = `${timestamp}-${randomNum}`; return uniqueId; } } // A sunburst is similar to a treemap, except it uses a radial layout. // The root node of the tree is at the center, with leaves on the circumference. // The area (or angle, depending on implementation) of each arc corresponds to its value. // Sunburst design by John Stasko. Data courtesy Jeff Heer. // http://bl.ocks.org/mbostock/4348373 function sunburst(recipient, data, width, height, tooltip = true) { if (width === "auto") { width = $(recipient).innerWidth(); } if (height === "auto") { height = width; } var radius = Math.min(width, height) / 2 - 40; var x = d3.scale.linear().range([0, 2 * Math.PI]); var y = d3.scale.sqrt().range([0, radius]); var color = d3.scale.category20c(); var svg = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height); var partition = d3.layout.partition().value(function(d) { return d.size; }); var arc = d3.svg .arc() .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); }) .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); }) .innerRadius(function(d) { return Math.max(0, y(d.y)); }) .outerRadius(function(d) { if (d.children || d.depth === 4) { return Math.max(0, y(d.y + d.dy)); } else { return Math.max(0, y(d.y + d.dy)) + 20; } }); var g = svg .selectAll("g") .data(partition.nodes(data)) .enter() .append("g") .attr( "transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")" ); var path = g .append("path") .attr("d", arc) .style("fill", function(d) { return d.color ? d3.rgb(d.color) : color((d.children ? d : d.parent).name); }) .style("cursor", "pointer") .style("stroke-width", "0.2") .on("click", click) .on("mouseover", tooltip === "1" ? over_user : "") .on("mouseout", out_user) .on("mousemove", move_tooltip); function computeTextRotation(d) { if (d.type === "central_service") { return 0; } var ang = ((x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI) * 180; if (calculate_angle(d) < 20) { return ang; } else { return Math.trunc(ang) == 90 || Math.trunc(ang) == 89 ? ang - 90 : 90 + ang; } } var text = g .append("text") .attr("transform", function(d) { if (typeof d.show_name != "undefined" && d.show_name) { return ( "translate(" + arc.centroid(d) + ")rotate(" + computeTextRotation(d) + ")" ); } }) .attr("x", function(d) { if (typeof d.show_name != "undefined" && d.show_name) { if (calculate_angle(d) < 20) { return (d.name.length + 15) * -1; } else { return (d.name.length + 25) * -1; } } }) .attr("dx", "6") // margin .attr("dy", function(d) { if (d.type === "central_service") { return "-7em"; } return ".35em"; }) // vertical-align .attr("opacity", function(d) { if (typeof d.show_name != "undefined" && d.show_name) { return 1; } else { return 0; } }) .text(function(d) { if (d.name.length > 20) { var resta = d.name.length - 12; var string = d.name.slice( d.name.length / 2 - resta / 2, d.name.length / 2 + resta / 2 ); var split = d.name.split(`${string}`); return `${split[0]}...${split[1]}`; } return d.name; }) .style("font-size", "11px") .style("fill", function(d) { if (d.color !== "#82b92e") { return "white"; } }) // Makes svg elements invisible to events .style("pointer-events", "none"); function click(d) { if (typeof d.link != "undefined") { window.location.href = d.link; } else { // fade out all text elements if (d.type === "central_service") { text.transition().attr("opacity", 1); } else { text.transition().attr("opacity", 0); } path .transition() .duration(750) .attrTween("d", arcTween(d)) .each("end", function(e, i) { // check if the animated element's data e lies within the visible angle span given in d if ( typeof e.type != "undefined" && (e.type == "group" || (e.type == "agent" && (d.type == "group" || d.type == "agent" || d.type == "module_group" || d.type == "module")) || ((e.type == "module_group" || e.type == "module") && (d.type == "agent" || d.type == "module_group"))) && e.x >= d.x && e.x < d.x + d.dx ) { // get a selection of the associated text element var arcText = d3.select(this.parentNode).select("text"); // fade in the text element and recalculate positions arcText .attr("transform", function(d) { return ( "translate(" + arc.centroid(d) + ")rotate(" + computeTextRotation(d) + ")" ); }) .attr("x", function(d) { if (calculate_angle(d) < 20) { return (d.name.length + 15) * -1; } else { return (d.name.length + 25) * -1; } }) .transition() .duration(250) .attr("opacity", 1); } }); } } d3.select(self.frameElement).style("height", height + "px"); // Interpolate the scales! function arcTween(d) { var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]), yd = d3.interpolate(y.domain(), [d.y, 1]), yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]); return function(d, i) { return i ? function(t) { return arc(d); } : function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); }; }; } function move_tooltip(d) { var x = d3.event.pageX + 10 - $("#menu_full").width(); var y = d3.event.pageY - 90; $("#tooltip").css("left", x + "px"); $("#tooltip").css("top", y + "px"); } function over_user(d) { id = d.id; $("#" + id).css("border", "1px solid black"); $("#" + id).css("z-index", "1"); show_tooltip(d); } function out_user(d) { id = d.id; $("#" + id).css("border", ""); $("#" + id).css("z-index", ""); hide_tooltip(); } function create_tooltip(d, x, y) { var tooltip = typeof d.tooltip_content != "undefined" ? d.tooltip_content : d.name; if ($("#tooltip").length == 0) { $(recipient).append( $("
") .attr("id", "tooltip") .html(tooltip) ); } else { $("#tooltip").html(tooltip); } $("#tooltip").attr( "style", "background: #fff;" + "color: #111;" + "position: absolute;" + "display: block;" + "width: 200px;" + "text-align: left;" + "padding: 10px 10px 10px 10px;" + "z-index: 2;" + "-webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "-moz-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);" + "left: " + 100 + "px;" + "top: " + 100 + "px;" ); } function show_tooltip(d) { var x = d3.event.pageX + 10; var y = d3.event.pageY + 10; create_tooltip(d, x, y); } function hide_tooltip() { $("#tooltip").hide(); } function calculate_angle(d) { var start_angle = Math.max(0, Math.min(2 * Math.PI, x(d.x))); start_angle = (start_angle * 180) / Math.PI; var end_angle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); end_angle = (end_angle * 180) / Math.PI; return end_angle - start_angle; } } function createGauge( name, etiqueta, value, min, max, min_warning, max_warning, warning_inverse, min_critical, max_critical, critical_inverse, font_size, height, font, transitionDuration ) { var gauges; var config = { size: height, label: etiqueta, min: undefined != min ? min : 0, max: undefined != max ? max : 100, font_size: font_size, transitionDuration: transitionDuration }; if (value == null) { config.majorTicks = 1; config.minorTicks = 1; value = false; } else { config.minorTicks = 10; } save_min_critical = min_critical; save_max_critical = max_critical; save_min_warning = min_warning; save_max_warning = max_warning; if (max_warning < config.min) { config.min = max_warning; } if (min_warning < config.min) { config.min = min_warning; } if (max_critical < config.min) { config.min = max_critical; } if (min_critical < config.min) { config.min = min_critical; } if (max_warning > config.max) { config.max = max_warning; } if (min_warning > config.max) { config.max = min_warning; } if (max_critical > config.max) { config.max = max_critical; } if (min_critical > config.max) { config.max = min_critical; } if (config.max < value) { config.max = value; } if (config.min > value) { config.min = value; } if (critical_inverse == 1) { if (max_critical == 0) { max_critical = min_critical; min_critical = config.min; } else { max_critical = save_min_critical; min_critical = config.min; max_critical2 = config.max; min_critical2 = save_max_critical; } } else { if (min_critical > max_critical && max_critical == 0) { max_critical = config.max; } } if (warning_inverse == 1) { if (max_warning == 0) { max_warning = min_warning; min_warning = config.min; } else { max_warning = save_min_warning; min_warning = config.min; max_warning2 = config.max; min_warning2 = save_max_warning; } } else { if (min_warning > max_warning && max_warning == 0) { max_warning = config.max; } } if (value !== false) { if (typeof max_warning2 !== "undefined") { if (min_warning >= 0 && min_warning != max_warning) { config.yellowZones = [ { from: min_warning, to: max_warning }, { from: min_warning2, to: max_warning2 } ]; } } else { if (min_warning >= 0 && min_warning != max_warning) { config.yellowZones = [{ from: min_warning, to: max_warning }]; } } if (typeof max_critical2 !== "undefined") { if (min_critical >= 0 && min_critical != max_critical) { config.redZones = [ { from: min_critical, to: max_critical }, { from: min_critical2, to: max_critical2 } ]; } } else { if (min_critical >= 0 && min_critical != max_critical) { config.redZones = [{ from: min_critical, to: max_critical }]; } } } var range = config.max - config.min; gauges = new Gauge(name, config, font); gauges.render(); gauges.redraw(value, config.transitionDuration); $(".gauge>text").each(function() { label = $(this).text(); if (!isNaN(label)) { label = parseFloat(label); text = label.toLocaleString(); if (label >= 1000000) text = text.substring(0, 4) + "M"; else if (label >= 100000) text = text.substring(0, 3) + "K"; else if (label >= 10000) text = text.substring(0, 3) + "K"; $(this).text(text); } }); $(".pointerContainer>text").each(function() { label = $(this).text(); if (!isNaN(label)) { label = parseFloat(label); text = label.toLocaleString(); if (label >= 10000000) text = text.substring(0, 4) + "M"; else if (label >= 1000000) text = text.substring(0, 4) + "M"; else if (label >= 100000) text = text.substring(0, 3) + "K"; else if (label >= 10000) text = text.substring(0, 3) + "K"; $(this).text(text); } }); } function createGauges( data, width, height, font_size, no_data_image, font, transitionDuration ) { var nombre, label, minimun_warning, maximun_warning, minimun_critical, maximun_critical, mininum, maxinum, valor; for (var key in data) { nombre = data[key].gauge; label = data[key].label; label = label.replace(/ /g, " "); label = label.replace(/\(/g, "("); label = label.replace(/\)/g, ")"); label = label.replace(/(/g, "("); label = label.replace(/)/g, ")"); minimun_warning = round_with_decimals(parseFloat(data[key].min_warning)); maximun_warning = round_with_decimals(parseFloat(data[key].max_warning)); minimun_critical = round_with_decimals(parseFloat(data[key].min_critical)); maximun_critical = round_with_decimals(parseFloat(data[key].max_critical)); mininum = round_with_decimals(parseFloat(data[key].min)); maxinum = round_with_decimals(parseFloat(data[key].max)); var critical_inverse = parseInt(data[key].critical_inverse); var warning_inverse = parseInt(data[key].warning_inverse); valor = round_with_decimals(data[key].value); if (isNaN(valor)) valor = null; createGauge( nombre, label, valor, mininum, maxinum, minimun_warning, maximun_warning, warning_inverse, minimun_critical, maximun_critical, critical_inverse, font_size, height, font, transitionDuration ); } } function Gauge(placeholderName, configuration, font) { var font = font .split("/") .pop() .split(".") .shift(); this.placeholderName = placeholderName; var self = this; // for internal d3 functions this.configure = function(configuration) { this.config = configuration; this.config.size = this.config.size * 0.9; this.config.font_size = this.config.font_size; this.config.raduis = (this.config.size * 0.97) / 2; this.config.cx = this.config.size / 2; this.config.cy = this.config.size / 2; this.config.min = undefined != configuration.min ? configuration.min : 0; this.config.max = undefined != configuration.max ? configuration.max : 100; this.config.range = this.config.max - this.config.min; this.config.majorTicks = configuration.majorTicks || 5; this.config.minorTicks = configuration.minorTicks || 2; this.config.greenColor = configuration.greenColor || "#109618"; this.config.yellowColor = configuration.yellowColor || "#FF9900"; this.config.redColor = configuration.redColor || "#DC3912"; this.config.transitionDuration = configuration.transitionDuration; }; this.render = function() { this.body = d3 .select("#" + this.placeholderName) .append("svg:svg") .attr("class", "gauge") .attr("width", this.config.size) .attr("height", this.config.size); this.body .append("svg:circle") .attr("cx", this.config.cx) .attr("cy", this.config.cy) .attr("r", this.config.raduis) .style("fill", "#ccc") .style("stroke", "#000") .style("stroke-width", "0.5px"); this.body .append("svg:circle") .attr("cx", this.config.cx) .attr("cy", this.config.cy) .attr("r", 0.9 * this.config.raduis) .style("fill", "#fff") .style("stroke", "#e0e0e0") .style("stroke-width", "2px"); for (var index in this.config.greenZones) { this.drawBand( this.config.greenZones[index].from, this.config.greenZones[index].to, self.config.greenColor ); } for (var index in this.config.yellowZones) { this.drawBand( this.config.yellowZones[index].from, this.config.yellowZones[index].to, self.config.yellowColor ); } for (var index in this.config.redZones) { this.drawBand( this.config.redZones[index].from, this.config.redZones[index].to, self.config.redColor ); } if (undefined != this.config.label) { var fontSize = Math.round(this.config.size / 9); this.body .append("svg:text") .attr("x", this.config.cx) .attr("y", this.config.cy / 2 + fontSize / 2) .attr("dy", fontSize / 2) .attr("text-anchor", "middle") .attr("class", font) .text(this.config.label) .style("font-size", this.config.font_size + "pt") .style("fill", "#333") .style("stroke-width", "0px"); } var fontSize = Math.round(this.config.size / 16); var majorDelta = this.config.range / (this.config.majorTicks - 1); for ( var major = this.config.min; major <= this.config.max; major += majorDelta ) { var minorDelta = majorDelta / this.config.minorTicks; for ( var minor = major + minorDelta; minor < Math.min(major + majorDelta, this.config.max); minor += minorDelta ) { var point1 = this.valueToPoint(minor, 0.75); var point2 = this.valueToPoint(minor, 0.85); this.body .append("svg:line") .attr("x1", point1.x) .attr("y1", point1.y) .attr("x2", point2.x) .attr("y2", point2.y) .style("stroke", "#666") .style("stroke-width", "1px"); } var point1 = this.valueToPoint(major, 0.7); var point2 = this.valueToPoint(major, 0.85); this.body .append("svg:line") .attr("x1", point1.x) .attr("y1", point1.y) .attr("x2", point2.x) .attr("y2", point2.y) .style("stroke", "#333") .style("stroke-width", "2px"); if (major == this.config.min || major == this.config.max) { var point = this.valueToPoint(major, 0.63); this.body .append("svg:text") .attr("x", point.x) .attr("y", point.y) .attr("dy", fontSize / 3) .attr("text-anchor", major == this.config.min ? "start" : "end") .text(major) .style("font-size", this.config.font_size + "pt") .style("fill", "#333") .style("stroke-width", "0px"); } } var pointerContainer = this.body .append("svg:g") .attr("class", "pointerContainer"); var midValue = (this.config.min + this.config.max) / 2; var pointerPath = this.buildPointerPath(midValue); var pointerLine = d3.svg .line() .x(function(d) { return d.x; }) .y(function(d) { return d.y; }) .interpolate("basis"); pointerContainer .selectAll("path") .data([pointerPath]) .enter() .append("svg:path") .attr("d", pointerLine) .style("fill", "#dc3912") .style("stroke", "#c63310") .style("fill-opacity", 0.7); pointerContainer .append("svg:circle") .attr("cx", this.config.cx) .attr("cy", this.config.cy) .attr("r", 0.12 * this.config.raduis) .style("fill", "#4684EE") .style("stroke", "#666") .style("opacity", 1); var fontSize = Math.round(this.config.size / 10); pointerContainer .selectAll("text") .data([midValue]) .enter() .append("svg:text") .attr("x", this.config.cx) .attr("y", this.config.size - this.config.cy / 4 - fontSize) .attr("dy", fontSize / 2) .attr("text-anchor", "middle") .style("font-size", this.config.font_size + "pt") .style("fill", "#000") .style("stroke-width", "0px"); this.redraw(this.config.min, 0); }; this.buildPointerPath = function(value) { var delta = this.config.range / 13; var head = valueToPoint(value, 0.85); var head1 = valueToPoint(value - delta, 0.12); var head2 = valueToPoint(value + delta, 0.12); var tailValue = value - (this.config.range * (1 / (270 / 360))) / 2; var tail = valueToPoint(tailValue, 0.28); var tail1 = valueToPoint(tailValue - delta, 0.12); var tail2 = valueToPoint(tailValue + delta, 0.12); return [head, head1, tail2, tail, tail1, head2, head]; function valueToPoint(value, factor) { var point = self.valueToPoint(value, factor); point.x -= self.config.cx; point.y -= self.config.cy; return point; } }; this.drawBand = function(start, end, color) { if (start === undefined) return; if (end === undefined) return; if (0 >= end - start) return; this.body .append("svg:path") .style("fill", color) .attr( "d", d3.svg .arc() .startAngle(this.valueToRadians(start)) .endAngle(this.valueToRadians(end)) .innerRadius(Math.round(0.65 * this.config.raduis)) .outerRadius(Math.round(0.85 * this.config.raduis)) ) .attr("transform", function() { return ( "translate(" + self.config.cx + ", " + self.config.cy + ") rotate(270)" ); }); }; this.redraw = function(value, transitionDuration) { var pointerContainer = this.body.select(".pointerContainer"); pointerContainer.selectAll("text").text(round_with_decimals(value)); var pointer = pointerContainer.selectAll("path"); pointer .transition() .duration(undefined != transitionDuration ? transitionDuration : 0) //.delay(0) //.ease("linear") //.attr("transform", function(d) .attrTween("transform", function() { var pointerValue = value; if (value > self.config.max) pointerValue = self.config.max + 0.02 * self.config.range; else if (value < self.config.min) pointerValue = self.config.min - 0.02 * self.config.range; var targetRotation = self.valueToDegrees(pointerValue) - 90; var currentRotation = self._currentRotation || targetRotation; self._currentRotation = targetRotation; return function(step) { var rotation = currentRotation + (targetRotation - currentRotation) * step; return ( "translate(" + self.config.cx + ", " + self.config.cy + ") rotate(" + rotation + ")" ); }; }); }; this.valueToDegrees = function(value) { // thanks @closealert //return value / this.config.range * 270 - 45; return ( (value / this.config.range) * 270 - ((this.config.min / this.config.range) * 270 + 45) ); }; this.valueToRadians = function(value) { return (this.valueToDegrees(value) * Math.PI) / 180; }; this.valueToPoint = function(value, factor) { return { x: this.config.cx - this.config.raduis * factor * Math.cos(this.valueToRadians(value)), y: this.config.cy - this.config.raduis * factor * Math.sin(this.valueToRadians(value)) }; }; // initialization this.configure(configuration); } function print_phases_donut(recipient, phases, width, height) { var svg = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height) .append("g"); svg.append("g").attr("class", "slices"); svg.append("g").attr("class", "labels"); svg.append("g").attr("class", "lines"); var radius = Math.min(width, height) / 2 - 50; var pie = d3.layout .pie() .sort(null) .value(function(d) { return parseFloat(d.label2); }); var arc = d3.svg .arc() .outerRadius(radius * 0.8) .innerRadius(radius * 0.4); var outerArc = d3.svg .arc() .innerRadius(radius * 0.9) .outerRadius(radius * 0.9); svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var key = function(d) { return d.data.label; }; function phasesData() { return phases.map(function(phase) { return { label: phase_name(phase, 1), label2: phase_name(phase, 2), value: phase.status }; }); } function phase_name(phase, index) { if (index == 1) { return phase.phase_name; } else { return phase.phase_time; } } print_phases(phasesData()); function print_phases(data) { /* ------- PIE SLICES -------*/ var slice = svg .select(".slices") .selectAll("path.slice") .data(pie(data), key); slice .enter() .insert("path") .style("fill", function(d) { if (d.data.value == 0) { return "#82b92e"; } else { return "#e63c52"; } }) .attr("class", "slice"); slice .transition() .duration(0) .attrTween("d", function(d) { this._current = this._current || d; var interpolate = d3.interpolate(this._current, d); this._current = interpolate(0); return function(t) { return arc(interpolate(t)); }; }); slice.exit().remove(); /* ------- TEXT LABELS -------*/ var text = svg .select(".labels") .selectAll("text") .data(pie(data), key); text .enter() .append("text") .append("tspan") .attr("dy", ".1em") .text(function(d) { return d.data.label; }) .style("font-size", "15px") .append("tspan") .attr("dy", "1.2em") .attr("dx", "-2.8em") .text(function(d) { return d.data.label2 + "ms"; }) .style("font-size", "15px"); function midAngle(d) { return d.startAngle + (d.endAngle - d.startAngle) / 2; } var ex = 1; var sum = 0; text .transition() .duration(0) .attrTween("transform", function(d) { this._current = this._current || d; var interpolate = d3.interpolate(this._current, d); this._current = interpolate(0); return function(t) { var d2 = interpolate(t); //fix for labels of a very small portion increase the //height of the label so that they do not overlap if (d2.endAngle - d2.startAngle < 0.1) { var pos = outerArc.centroid(d2); if (ex % 2 == 0) { pos[0] = 150; } else { pos[0] = -150; sum++; } pos[1] = pos[1] - 35 * sum; ex++; } else { var pos = outerArc.centroid(d2); pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1); } return "translate(" + pos + ")"; }; }) .styleTween("text-anchor", function(d) { this._current = this._current || d; var interpolate = d3.interpolate(this._current, d); this._current = interpolate(0); return function(t) { var d2 = interpolate(t); //fix for labels of a very small portion increase the //height of the label so that they do not overlap if (d2.endAngle - d2.startAngle < 0.1) { if (ex % 2 == 0) { return "start"; } else { return "end"; } } return midAngle(d2) < Math.PI ? "start" : "end"; }; }); text.exit().remove(); /* ------- SLICE TO TEXT POLYLINES -------*/ var polyline = svg .select(".lines") .selectAll("polyline") .data(pie(data), key); polyline.enter().append("polyline"); var ex2 = 1; var sum2 = 0; polyline .transition() .duration(0) .attrTween("points", function(d) { this._current = this._current || d; var interpolate = d3.interpolate(this._current, d); this._current = interpolate(0); return function(t) { var d2 = interpolate(t); //fix for labels of a very small portion increase the //height of the label so that they do not overlap if (d2.endAngle - d2.startAngle < 0.1) { var pos = outerArc.centroid(d2); if (ex2 % 2 == 0) { pos[0] = 150 * 0.95; } else { pos[0] = -150 * 0.95; sum2++; } pos[1] = pos[1] - 30 * sum2; ex2++; } else { var pos = outerArc.centroid(d2); pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1); } return [arc.centroid(d2), outerArc.centroid(d2), pos]; }; }) .style("stroke", "black") .style("opacity", ".3") .style("stroke-width", "2px") .style("fill", "none"); polyline.exit().remove(); } } function progress_bar_d3( recipient, percentile, width, height, color, unit, label, label_color, radiusx, radiusy, transition ) { var startPercent = 0; var endPercent = parseInt(percentile) / 100; var count = Math.abs((endPercent - startPercent) / 0.01); var step = endPercent < startPercent ? -0.01 : 0.01; var circle = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height); var progress_back = circle .append("rect") .attr("fill", "#000000") .attr("fill-opacity", 0.5) .attr("height", height) .attr("width", width) .attr("rx", radiusx) .attr("ry", radiusy) .attr("x", 0); var progress_front = circle .append("rect") .attr("fill", color) .attr("fill-opacity", 1) .attr("height", height) .attr("width", 0) .attr("rx", radiusx) .attr("ry", radiusy) .attr("x", 0); var labelText = circle .append("text") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", 20) .html(label) .attr("dy", "15") .attr("text-anchor", "middle"); var numberText = circle .append("text") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", "#FFFFFF") .style("font-weight", "bold") .style("font-size", 14) .attr("text-anchor", "middle") .attr("dy", (height - height / 2) / 4); function updateProgress(bar_progress) { var percent_value = Number(bar_progress * 100); numberText.text(percent_value.toFixed() + " " + unit); progress_front.attr("width", width * bar_progress); } if (transition == 0) { var bar_progress = endPercent; updateProgress(bar_progress); } else { var bar_progress = startPercent; (function loops() { updateProgress(bar_progress); if (count > 0) { count--; bar_progress += step; setTimeout(loops, 30); } })(); } } function progress_bubble_d3( recipient, percentile, width, height, color, unit, label, label_color ) { var startPercent = 0; var endPercent = parseInt(percentile) / 100; var count = Math.abs((endPercent - startPercent) / 0.01); var step = endPercent < startPercent ? -0.01 : 0.01; var numberSize = 0; var textSize = 0; var unitSize = 0; var yPosText = 0; var yPosNumber = 0; if (width >= 500) { numberSize = 100; textSize = 50; unitSize = 50; yPosNumber = "15"; yPosText = "-100"; } else if (width >= 400) { numberSize = 80; textSize = 40; unitSize = 40; yPosNumber = "15"; yPosText = "-80"; } else if (width >= 300) { numberSize = 60; textSize = 30; unitSize = 30; yPosNumber = "15"; yPosText = "-45"; } else if (width >= 200) { numberSize = 40; textSize = 20; unitSize = 20; yPosNumber = "50"; yPosText = "-30"; } else if (width >= 100) { numberSize = 20; textSize = 10; unitSize = 10; yPosNumber = "5"; yPosText = "-20"; } else { numberSize = 10; textSize = 8; unitSize = 8; yPosNumber = "5"; yPosText = "-10"; } var circle = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height); var progress_back = circle .append("circle") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", "#000000") .attr("fill-opacity", 0) .attr("r", width / 2); var progress_front = circle .append("circle") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", color) .attr("fill-opacity", 1) .attr("r", 0); var labelText = circle .append("text") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", textSize) .html(label) .attr("dy", -(width / 3)) .attr("text-anchor", "middle"); var numberText = circle .append("text") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", numberSize) .attr("text-anchor", "middle") .attr("dy", width / 3); function updateProgress(bar_progress) { var percent_value = Number(bar_progress * 100); numberText.text(percent_value.toFixed() + " " + unit); progress_front.attr("r", (width / 2) * bar_progress); } var bar_progress = startPercent; (function loops() { updateProgress(bar_progress); if (count > 0) { count--; bar_progress += step; setTimeout(loops, 30); } })(); } function print_circular_progress_bar( recipient, percentile, width, height, color, unit, label, label_color, transition ) { var twoPi = Math.PI * 2; var radius = width / 2; var border = 20; var startPercent = 0; var endPercent = parseInt(percentile) / 100; var count = Math.abs((endPercent - startPercent) / 0.01); var step = endPercent < startPercent ? -0.01 : 0.01; var numberSize = 0; var textSize = 0; var unitSize = 0; var yPosText = 0; var yPosUnit = 0; var yPosNumber = 0; if (width >= 500) { numberSize = 100; textSize = 50; unitSize = 50; yPosNumber = "15"; yPosText = "-100"; yPosUnit = "100"; } else if (width >= 400) { numberSize = 80; textSize = 40; unitSize = 40; yPosNumber = "15"; yPosText = "-80"; yPosUnit = "80"; } else if (width >= 300) { numberSize = 60; textSize = 30; unitSize = 30; yPosNumber = "15"; yPosText = "-45"; yPosUnit = "60"; } else if (width >= 200) { numberSize = 40; textSize = 20; unitSize = 20; yPosNumber = "10"; yPosText = "-30"; yPosUnit = "40"; } else if (width >= 100) { numberSize = 20; textSize = 10; unitSize = 10; yPosNumber = "5"; yPosText = "-15"; yPosUnit = "20"; } else { numberSize = 8; textSize = 4; unitSize = 4; yPosNumber = "2"; yPosText = "-5"; yPosUnit = "5"; } var arc = d3.svg .arc() .startAngle(0) .innerRadius(radius) .outerRadius(radius - border); var circle = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")"); var meter = circle.append("g").attr("class", "progress-meter"); meter .append("path") .attr("fill", "#000000") .attr("fill-opacity", 0.5) .attr("d", arc.endAngle(twoPi)); var foreground = circle .append("path") .attr("fill", color) .attr("fill-opacity", 1) .attr("stroke", color) .attr("stroke-opacity", 1); var front = circle .append("path") .attr("fill", color) .attr("fill-opacity", 1); var labelText = circle .append("text") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", textSize) .html(label) .attr("text-anchor", "middle") .attr("dy", yPosText); var numberText = circle .append("text") .attr("fill", "#333333") .style("font-weight", "bold") .style("font-size", numberSize) .attr("text-anchor", "middle") .attr("dy", yPosNumber); var percentText = circle .append("text") .attr("fill", "#333333") .style("font-weight", "bold") .style("font-size", unitSize) .text(unit) .attr("text-anchor", "middle") .attr("dy", yPosUnit); function updateProgress(progress) { foreground.attr("d", arc.endAngle(twoPi * progress)); front.attr("d", arc.endAngle(twoPi * progress)); var percent_value = Number(progress * 100); numberText.text(percent_value.toFixed()); } var progress = startPercent; if (transition == 0) updateProgress(endPercent); else { (function loops() { updateProgress(progress); if (count > 0) { count--; progress += step; setTimeout(loops, 30); } })(); } } function print_interior_circular_progress_bar( recipient, percentile, width, height, color, unit, label, label_color ) { var twoPi = Math.PI * 2; var radius = width / 2 - 20; var radius2 = width / 2; var border = 20; var startPercent = 0; var endPercent = parseInt(percentile) / 100; var count = Math.abs((endPercent - startPercent) / 0.01); var step = endPercent < startPercent ? -0.01 : 0.01; var numberSize = 0; var textSize = 0; var unitSize = 0; var yPosText = 0; var yPosUnit = 0; var yPosNumber = 0; if (width >= 500) { numberSize = 100; textSize = 50; unitSize = 50; yPosNumber = "15"; yPosText = "-100"; yPosUnit = "100"; } else if (width >= 400) { numberSize = 80; textSize = 40; unitSize = 40; yPosNumber = "15"; yPosText = "-80"; yPosUnit = "80"; } else if (width >= 300) { numberSize = 60; textSize = 30; unitSize = 30; yPosNumber = "15"; yPosText = "-45"; yPosUnit = "60"; } else if (width >= 200) { numberSize = 40; textSize = 20; unitSize = 20; yPosNumber = "10"; yPosText = "-30"; yPosUnit = "40"; } else if (width >= 100) { numberSize = 20; textSize = 10; unitSize = 10; yPosNumber = "5"; yPosText = "-15"; yPosUnit = "20"; } else { numberSize = 8; textSize = 4; unitSize = 4; yPosNumber = "2"; yPosText = "-5"; yPosUnit = "5"; } var arc = d3.svg .arc() .startAngle(0) .innerRadius(radius) .outerRadius(radius - border); var arc2 = d3.svg .arc() .startAngle(0) .innerRadius(radius2) .outerRadius(radius2 - border); var circle = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")"); var meter = circle.append("g").attr("class", "progress-meter"); meter .append("path") .attr("fill", "#000000") .attr("fill-opacity", 0.5) .attr("d", arc.endAngle(twoPi)); var meter = circle.append("g").attr("class", "progress-meter"); meter .append("path") .attr("fill", color) .attr("fill-opacity", 1) .attr("d", arc2.endAngle(twoPi)); var foreground = circle .append("path") .attr("fill", color) .attr("fill-opacity", 1) .attr("stroke", color) .attr("stroke-opacity", 1); var front = circle .append("path") .attr("fill", color) .attr("fill-opacity", 1); var labelText = circle .append("text") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", textSize) .html(label) .attr("text-anchor", "middle") .attr("dy", yPosText); var numberText = circle .append("text") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", numberSize) .attr("text-anchor", "middle") .attr("dy", yPosNumber); var percentText = circle .append("text") .attr("fill", label_color) .style("font-weight", "bold") .style("font-size", unitSize) .text(unit) .attr("text-anchor", "middle") .attr("dy", yPosUnit); function updateProgress(progress) { foreground.attr("d", arc.endAngle(twoPi * progress)); front.attr("d", arc.endAngle(twoPi * progress)); var percent_value = Number(progress * 100); numberText.text(percent_value.toFixed()); } var progress = startPercent; (function loops() { updateProgress(progress); if (count > 0) { count--; progress += step; setTimeout(loops, 30); } })(); } // eslint-disable-next-line no-unused-vars function print_donut_graph( recipient, width, height, module_data, resume_color ) { var svg = d3 .select(recipient) .append("svg") .attr("width", width) .attr("height", height) .append("g"); svg.append("g").attr("class", "slices"); var heightLegend = 25 * module_data.length; var maxRadius = (height - heightLegend) / 2; var radius = maxRadius; if (maxRadius > width / 2) { radius = width / 2; } var arc = d3.svg .arc() .outerRadius(radius * 0.8) .innerRadius(radius * 0.4); var key = function(d) { return d.data.label; }; var pie = d3.layout .pie() .sort(null) .value(function(d) { return parseFloat(d.percent); }); jQuery.each(module_data, function(key, m_d) { svg .append("g") .append("rect") .attr("fill", m_d.color) .attr("x", 20) .attr("y", 20 * (key + 1)) .attr("width", 25) .attr("height", 15); svg .append("g") .append("text") .attr("fill", resume_color) .attr("transform", "translate(" + 40 + "," + 20 * (key + 1) + ")") .attr("x", 15) .attr("y", 10) .text(m_d.tag_name) .style("font-size", "7pt"); }); function donutData() { return module_data.map(function(m_data) { return { label: m_data.tag_name, percent: m_data.percent, color: m_data.color }; }); } print_phases(donutData()); function print_phases(data) { var slice = svg .select(".slices") .selectAll("path.slice") .data(pie(data), key); slice .enter() .insert("path") .style("fill", function(d) { return d.data.color; }) .attr("class", "slice") .attr( "transform", "translate(" + width / 2 + "," + (height + heightLegend) / 2 + ")" ); slice .transition() .duration(0) .attrTween("d", function(d) { this._current = this._current || d; var interpolate = d3.interpolate(this._current, d); this._current = interpolate(0); return function(t) { return arc(interpolate(t)); }; }); slice.exit().remove(); } } function printClockAnalogic1( time_format, timezone, clock_animation, width, height, id_element, color ) { if (width != 0) { width = width - 20; height = width - 20; } if (width == 0) { width = 180; height = 180; } var radians = 0.0174532925, clockRadius = width / 2, margin = 10, width = (clockRadius + margin) * 2, height = (clockRadius + margin) * 2, hourHandLength = (2 * clockRadius) / 3, minuteHandLength = clockRadius, secondHandLength = clockRadius - 12, secondHandBalance = 30, secondTickStart = clockRadius; (secondTickLength = -10), (hourTickStart = clockRadius), (hourTickLength = -18), (secondLabelRadius = clockRadius + 16), (secondLabelYOffset = 5), (hourLabelRadius = clockRadius - 40), (hourLabelYOffset = 7); var hourScale = d3.scale .linear() .range([0, 330]) .domain([0, 11]); var minuteScale = (secondScale = d3.scale .linear() .range([0, 354]) .domain([0, 59])); var handData = [ { type: "hour", value: 0, length: -hourHandLength, scale: hourScale }, { type: "minute", value: 0, length: -minuteHandLength, scale: minuteScale }, { type: "second", value: 0, length: -secondHandLength, scale: secondScale, balance: secondHandBalance } ]; function drawClock() { //create all the clock elements updateData(timezone); //draw them in the correct starting position var svg = d3 .select("#clock_" + id_element) .append("svg") .attr("width", width) .attr("height", height); var face = svg .append("g") .attr("id", "clock-face") .attr("class", "invert_filter") .attr( "transform", "translate(" + (clockRadius + margin) + "," + (clockRadius + margin) + ")" ); //add marks for seconds face .selectAll(".second-tick") .data(d3.range(0, 60)) .enter() .append("line") .attr("class", "second-tick") .attr("x1", 0) .attr("x2", 0) .attr("y1", secondTickStart) .attr("y2", secondTickStart + secondTickLength) .attr("stroke", color) .attr("transform", function(d) { return "rotate(" + secondScale(d) + ")"; }); //and labels // face.selectAll('.second-label') // .data(d3.range(5,61,5)) // .enter() // .append('text') // .attr('class', 'second-label') // .attr('text-anchor','middle') // .attr('x',function(d){ // return secondLabelRadius*Math.sin(secondScale(d)*radians); // }) // .attr('y',function(d){ // return -secondLabelRadius*Math.cos(secondScale(d)*radians) + secondLabelYOffset; // }) // .text(function(d){ // return d; // }); //... and hours face .selectAll(".hour-tick") .data(d3.range(0, 12)) .enter() .append("line") .attr("class", "hour-tick") .attr("x1", 0) .attr("x2", 0) .attr("y1", hourTickStart) .attr("y2", hourTickStart + hourTickLength) .attr("stroke", color) .attr("transform", function(d) { return "rotate(" + hourScale(d) + ")"; }); face .selectAll(".hour-label") .data(d3.range(3, 13, 3)) .enter() .append("text") .attr("class", "hour-label") .attr("text-anchor", "middle") .attr("stroke", color) .attr("x", function(d) { return hourLabelRadius * Math.sin(hourScale(d) * radians); }) .attr("y", function(d) { return ( -hourLabelRadius * Math.cos(hourScale(d) * radians) + hourLabelYOffset ); }) .text(function(d) { return d; }); var hands = face.append("g").attr("id", "clock-hands"); face .append("g") .attr("id", "face-overlay") .append("circle") .attr("class", "hands-cover") .attr("stroke", color) .attr("x", 0) .attr("y", 0) .attr("r", clockRadius / 20); hands .selectAll("line") .data(handData) .enter() .append("line") .attr("stroke", color) .attr("class", function(d) { return d.type + "-hand"; }) .attr("x1", 0) .attr("y1", function(d) { return d.balance ? d.balance : 0; }) .attr("x2", 0) .attr("y2", function(d) { return d.length; }) .attr("transform", function(d) { return "rotate(" + d.scale(d.value) + ")"; }); } function moveHands() { d3.select("#clock_" + id_element + " #clock-hands") .selectAll("line") .data(handData) .transition() .attr("transform", function(d) { return "rotate(" + d.scale(d.value) + ")"; }); } function updateData(tz) { var d = new Date(); var dt = d.getTime(); os = d.getTimezoneOffset(); tz = parseInt(tz) + parseInt(os * 60); var t = new Date(dt + tz * 1000); handData[0].value = (t.getHours() % 12) + t.getMinutes() / 60; handData[1].value = t.getMinutes(); handData[2].value = t.getSeconds(); } drawClock(); setInterval(function() { updateData(timezone); moveHands(); }, 1000); d3.select(self.frameElement).style("height", height + "px"); $("#clock_" + id_element).css("margin-top", "0"); } function printClockDigital1( time_format, timezone, clock_animation, width, height, id_element, color ) { var svgUnderlay = d3.select("#clock_" + id_element + " svg"), svgOverlay = d3.select("#clock_" + id_element), svg = d3.selectAll("#clock_" + id_element + " svg"); svgUnderlay.attr("id", "underlay_" + id_element); svgUnderlay.attr("class", "invert_filter"); svgOverlay.attr("id", "overlay_" + id_element); var digit = svg.selectAll(".digit"), separator = svg.selectAll(".separator circle"); var digitPattern = [ [1, 0, 1, 1, 0, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 1, 1, 0, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 0, 1, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 1, 0, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 0, 1, 1] ]; (function tick() { var tz = timezone; var d = new Date(); var dt = d.getTime(); os = d.getTimezoneOffset(); tz = parseInt(tz) + parseInt(os * 60); var t = new Date(dt + tz * 1000); var now = new Date(), hours = t.getHours(), minutes = t.getMinutes(), seconds = t.getSeconds(); digit = digit.data([ (hours / 10) | 0, hours % 10, (minutes / 10) | 0, minutes % 10, (seconds / 10) | 0, seconds % 10 ]); digit.select("path:nth-child(1)").classed("lit", function(d) { return digitPattern[0][d]; }); digit.select("path:nth-child(2)").classed("lit", function(d) { return digitPattern[1][d]; }); digit.select("path:nth-child(3)").classed("lit", function(d) { return digitPattern[2][d]; }); digit.select("path:nth-child(4)").classed("lit", function(d) { return digitPattern[3][d]; }); digit.select("path:nth-child(5)").classed("lit", function(d) { return digitPattern[4][d]; }); digit.select("path:nth-child(6)").classed("lit", function(d) { return digitPattern[5][d]; }); digit.select("path:nth-child(7)").classed("lit", function(d) { return digitPattern[6][d]; }); separator.classed("lit", 1); setTimeout(tick, 1000 - (now % 1000)); })(); } function valueToBytes(value) { var shorts = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]; var pos = 0; while (value >= 1024) { // As long as the number can be divided by divider. pos++; // Position in array starting with 0. value = value / 1024; } // This will actually do the rounding and the decimals. return value.toFixed(2) + shorts[pos] + "B"; }