HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Federal Budget Allocation</title>
<link href="example.css" rel="stylesheet" type="text/css">
<script src="https://unpkg.com/tippy.js@3/dist/tippy.all.min.js"></script>
<script src="https://spreadsheets.google.com/feeds/cells/2PACX-1vTzHp6oLErcc8AsGeP97XsagpmFJ5s35Qb9emMDAQXjqaTJ-ZZEezsLg6F01DXjAWTs-eMVy_k9wQ5H/od6/public/values?alt=json-in-script&callback=setupPieChart"></script>
<script src="https://www.gstatic.com/firebasejs/5.5.2/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/firebaseui/3.1.1/firebaseui.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.1.1/firebaseui.css" />
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyCZOMxq15cvgitbHaCWiKHIt8SI4wHYyCE",
authDomain: "think-by-numbers.firebaseapp.com",
databaseURL: "https://think-by-numbers.firebaseio.com",
projectId: "think-by-numbers",
storageBucket: "",
messagingSenderId: "341920646928"
};
firebase.initializeApp(config);
// FirebaseUI config.
var uiConfig = {
signInSuccessUrl: '<url-to-redirect-to-on-success>',
signInOptions: [
// Leave the lines as is for the providers you want to offer your users.
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.FacebookAuthProvider.PROVIDER_ID,
firebase.auth.TwitterAuthProvider.PROVIDER_ID,
firebase.auth.GithubAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
firebase.auth.PhoneAuthProvider.PROVIDER_ID,
firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID
],
// tosUrl and privacyPolicyUrl accept either url string or a callback
// function.
// Terms of service url/callback.
tosUrl: '<your-tos-url>',
// Privacy policy url/callback.
privacyPolicyUrl: function() {
window.location.assign('<your-privacy-policy-url>');
}
};
// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);
</script>
<script src="draggable-piechart.js"></script>
<script src="budget.js"></script>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div id="piechart-controls">
<canvas id="piechart" width="400" height="400">Your browser is too old!</canvas>
<br>
<table id="proportions-table"></table>
<br>
<p id="piechart-instructions">
Drag the circles or click the buttons to adjust the pie chart. If a segment has gone,
you can get it back by clicking it's '+' button.
</p>
</div>
</body>
</html>
* {
font-family: Arial, sans-serif;
}
#piechart-controls {
text-align: center;
}
#piechart-instructions {
display: inline-block;
width: 600px;
}
table {
border-collapse: collapse;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
display: inline-block;
}
td, th {
border: 1px solid lightgray;
padding: 10px;
margin: 0;
}
.adjust-button {
width: 20px;
height: 20px;
font-size: 20px;
line-height: 20px;
text-align: center;
display: inline-block;
border: 1px solid grey;
border-radius: 5px;
margin: 1px;
}
var {
width: 40px;
display: inline-block;
}
/**
* Created by james on 23/02/2017.
*/
(function(){
var extend = function(out) {
out = out || {};
for (var i = 1; i < arguments.length; i++) {
if (!arguments[i])
continue;
for (var key in arguments[i]) {
if (arguments[i].hasOwnProperty(key))
out[key] = arguments[i][key];
}
}
return out;
};
var DraggablePiechart = function(setup) {
var piechart = this;
setup = extend({}, this.defaults, setup);
this.canvas = setup.canvas;
this.context = setup.canvas.getContext("2d");
if (!this.context) {
console.log('Error: DraggablePiechart needs an html5 canvas.');
return;
}
if (setup.proportions) {
this.data = generateDataFromProportions(setup.proportions);
} else if (setup.data) {
this.data = setup.data;
}
this.draggedPie = null;
this.hoveredIndex = -1;
this.radius = setup.radius;
this.collapsing = setup.collapsing;
this.minAngle = setup.minAngle;
this.drawSegment = setup.drawSegment;
this.drawNode = setup.drawNode;
this.onchange = setup.onchange;
// Bind appropriate events
if (is_touch_device()) {
this.canvas.addEventListener('touchstart', touchStart);
this.canvas.addEventListener('touchmove',touchMove);
document.addEventListener('touchend', touchEnd);
} else {
this.canvas.addEventListener('mousedown',touchStart);
this.canvas.addEventListener('mousemove',touchMove);
document.addEventListener('mouseup', touchEnd);
}
this.draw();
function touchStart(event) {
piechart.draggedPie = piechart.getTarget(getMouseLocation(event));
if (piechart.draggedPie) {
piechart.hoveredIndex = piechart.draggedPie.index;
}
}
function touchEnd() {
if (piechart.draggedPie) {
piechart.draggedPie = null;
piechart.draw();
}
}
function touchMove(event) {
var dragLocation = getMouseLocation(event);
if (!piechart.draggedPie) {
var hoveredTarget = piechart.getTarget(dragLocation);
if (hoveredTarget) {
piechart.hoveredIndex = hoveredTarget.index;
piechart.draw();
} else if (piechart.hoveredIndex !== -1) {
piechart.hoveredIndex = -1;
piechart.draw();
}
return;
}
var draggedPie = piechart.draggedPie;
var dx = dragLocation.x - draggedPie.centerX;
var dy = dragLocation.y - draggedPie.centerY;
// Get angle of grabbed target from centre of pie
var newAngle = Math.atan2(dy,dx) - draggedPie.angleOffset;
piechart.shiftSelectedAngle(newAngle);
piechart.draw();
}
function getMouseLocation(evt) {
var rect = piechart.canvas.getBoundingClientRect();
if (evt.clientX) {
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
}
} else {
return {
x: evt.originalEvent.targetTouches[0].clientX - rect.left,
y: evt.originalEvent.targetTouches[0].clientY - rect.top
}
}
}
/*
* Generates angle data from proportions (array of objects with proportion, format
*/
function generateDataFromProportions(proportions) {
// sum of proportions
var total = proportions.reduce(function(a, v) { return a + v.proportion; }, 0);
// begin at 0
var currentAngle = 0;
// use the proportions to reconstruct angles
return proportions.map(function(v, i) {
var arcSize = TAU * v.proportion / total;
var data = {
angle: currentAngle,
format: v.format,
collapsed: arcSize <= 0
};
currentAngle = normaliseAngle(currentAngle + arcSize);
return data;
});
}
};
/*
* Move angle specified by index: i, by amount: angle in rads
*/
DraggablePiechart.prototype.moveAngle = function(i, amount) {
if (this.data[i].collapsed && amount < 0) {
this.setCollapsed(i,false);
return;
}
var geometry = this.getGeometry();
this.draggedPie = {
index: i,
angleOffset: 0,
centerX: geometry.centerX,
centerY: geometry.centerY,
startingAngles: this.data.map(function(v){ return v.angle; }),
collapsed: this.data.map(function(v){ return v.collapsed; }),
angleDragDistance: 0
};
this.shiftSelectedAngle(this.data[i].angle + amount);
this.draggedPie = null;
this.draw();
};
/*
* Gets percentage of indexed slice
*/
DraggablePiechart.prototype.getSliceSizePercentage = function(index) {
var visibleSegments = this.getVisibleSegments();
for(var i = 0; i < visibleSegments.length; i += 1) {
if (visibleSegments[i].index === index) {
return 100 * visibleSegments[i].arcSize / TAU;
}
}
return 0;
};
/*
* Gets all percentages for each slice
*/
DraggablePiechart.prototype.getAllSliceSizePercentages = function() {
var visibleSegments = this.getVisibleSegments();
var percentages = [];
for(var i = 0; i < this.data.length; i += 1) {
if (this.data[i].collapsed) {
percentages[i] = 0;
} else {
for(var j = 0; j < visibleSegments.length; j += 1) {
if (visibleSegments[j].index === i) {
percentages[i] = 100 * visibleSegments[j].arcSize / TAU;
}
}
}
}
return percentages;
};
/*
* Gets the geometry of the pie chart in the canvas
*/
DraggablePiechart.prototype.getGeometry = function() {
var centerX = Math.floor(this.canvas.width / 2);
var centerY = Math.floor(this.canvas.height / 2);
return {
centerX: centerX,
centerY: centerY,
radius: Math.min(centerX, centerY) * this.radius
}
};
/*
* Returns a segment to drag if given a close enough location
*/
DraggablePiechart.prototype.getTarget = function(targetLocation) {
var geometry = this.getGeometry();
var startingAngles = [];
var collapsed = [];
var closest = {
index: -1,
distance: 9999999,
angle: null
};
for (var i = 0; i < this.data.length; i += 1) {
startingAngles.push(this.data[i].angle);
collapsed.push(this.data[i].collapsed);
if (this.data[i].collapsed) { continue; }
var dx = targetLocation.x - geometry.centerX;
var dy = targetLocation.y - geometry.centerY;
var trueGrabbedAngle = Math.atan2(dy,dx);
var distance = Math.abs(smallestSignedAngleBetween(trueGrabbedAngle, this.data[i].angle));
if (distance < closest.distance) {
closest.index = i;
closest.distance = distance;
closest.angle = trueGrabbedAngle;
}
}
if (closest.distance < 0.1) {
return {
index: closest.index,
angleOffset: smallestSignedAngleBetween(closest.angle, startingAngles[closest.index]),
centerX: geometry.centerX,
centerY: geometry.centerY,
startingAngles: startingAngles,
collapsed: collapsed,
angleDragDistance: 0
}
} else {
return null;
}
};
/*
* Sets segments collapsed or uncollapsed
*/
DraggablePiechart.prototype.setCollapsed = function(index, collapsed) {
// Flag to set position of previously collapsed to new location
var setNewPos = this.data[index].collapsed && !collapsed;
this.data[index].collapsed = collapsed;
var visibleSegments = this.getVisibleSegments();
// Shift other segments along to make space if necessary
for (var i = 0; i < visibleSegments.length; i += 1) {
// Start at this segment
if (visibleSegments[i].index === index) {
//Set new position
if (setNewPos) {
var nextSegment = visibleSegments[ mod(i + 1, visibleSegments.length) ];
this.data[index].angle = nextSegment.angle - this.minAngle;
}
for (var j = 0; j < (visibleSegments.length - 1); j += 1) {
var currentSegment = visibleSegments[ mod(1 + i - j, visibleSegments.length) ];
var nextAlongSegment = visibleSegments[ mod(i - j, visibleSegments.length) ];
var angleBetween = Math.abs(smallestSignedAngleBetween(this.data[currentSegment.index].angle, this.data[nextAlongSegment.index].angle) );
if (angleBetween < this.minAngle) {
this.data[nextAlongSegment.index].angle = normaliseAngle(this.data[currentSegment.index].angle - this.minAngle);
}
}
break;
}
}
this.draw();
};
/*
* Returns visible segments
*/
DraggablePiechart.prototype.getVisibleSegments = function() {
var piechart = this;
// Collect data for visible segments
var visibleSegments = [];
for (var i = 0; i < piechart.data.length; i += 1) {
if (!piechart.data[i].collapsed) {
var startingAngle = piechart.data[i].angle;
// Get arcSize
var foundNextAngle = false;
for (var j = 1; j < piechart.data.length; j += 1) {
var nextAngleIndex = (i + j) % piechart.data.length;
if (!piechart.data[nextAngleIndex].collapsed) {
var arcSize = piechart.data[nextAngleIndex].angle - startingAngle;
if (arcSize <= 0) { arcSize += TAU; }
visibleSegments.push({
arcSize: arcSize,
angle: startingAngle,
format: piechart.data[i].format,
index: i
});
foundNextAngle = true;
break;
}
}
// Only one segment
if (!foundNextAngle) {
visibleSegments.push({
arcSize: TAU,
angle: startingAngle,
format: piechart.data[i].format,
index: i
});
break;
}
}
}
return visibleSegments;
};
/*
* Returns invisible segments
*/
DraggablePiechart.prototype.getInvisibleSegments = function() {
var piechart = this;
// Collect data for visible segments
var invisibleSegments = [];
for (var i = 0; i < piechart.data.length; i += 1) {
if (piechart.data[i].collapsed) {
invisibleSegments.push({
index: i,
format: piechart.data[i].format
})
}
}
return invisibleSegments;
};
/*
* Draws the piechart
*/
DraggablePiechart.prototype.draw = function () {
var piechart = this;
var context = piechart.context;
var canvas = piechart.canvas;
context.clearRect(0, 0, canvas.width, canvas.height);
var geometry = this.getGeometry();
var visibleSegments = this.getVisibleSegments();
// Flags to get arc sizes and index of largest arc, for drawing order
var largestArcSize = 0;
var indexLargestArcSize = -1;
// Get the largeset arcsize
for (var i = 0; i < visibleSegments.length; i += 1) {
if (visibleSegments[i].arcSize > largestArcSize) {
largestArcSize = visibleSegments[i].arcSize;
indexLargestArcSize = i;
}
}
// Need to draw in correct order
for (i = 0; i < visibleSegments.length; i += 1) {
// Start with one *after* largest
var index = mod(i + indexLargestArcSize + 1, visibleSegments.length);
piechart.drawSegment(context, piechart, geometry.centerX, geometry.centerY, geometry.radius, visibleSegments[index].angle, visibleSegments[index].arcSize, visibleSegments[index].format, false);
}
// Now draw invisible segments
var invisibleSegments = this.getInvisibleSegments();
for (i = 0; i < invisibleSegments.length; i += 1) {
piechart.drawSegment(context, piechart, geometry.centerX, geometry.centerY, geometry.radius, 0, 0, invisibleSegments[i].format, true);
}
// Finally draw drag nodes on top (order not important)
for (i = 0; i < visibleSegments.length; i += 1) {
var location = polarToCartesian(visibleSegments[i].angle, geometry.radius);
piechart.drawNode(context, piechart, location.x, location.y, geometry.centerX, geometry.centerY, i === piechart.hoveredIndex);
}
piechart.onchange(piechart);
};
/*
* *INTERNAL USE ONLY*
* Moves the selected angle to a new angle
*/
DraggablePiechart.prototype.shiftSelectedAngle = function (newAngle) {
var piechart = this;
if (!piechart.draggedPie) { return; }
var draggedPie = piechart.draggedPie;
// Get starting angle of the target
var startingAngle = draggedPie.startingAngles[draggedPie.index];
// Get previous angle of the target
var previousAngle = piechart.data[draggedPie.index].angle;
// Get diff from grabbed target start (as -pi to +pi)
var angleDragDistance = smallestSignedAngleBetween(newAngle, startingAngle);
// Get previous diff
var previousDragDistance = draggedPie.angleDragDistance;
// Determines whether we go clockwise or anticlockwise
var rotationDirection = previousDragDistance > 0 ? 1 : -1;
// Reverse the direction if we have done over 180 in either direction
var sameDirection = previousDragDistance > 0 === angleDragDistance > 0;
var greaterThanHalf = Math.abs(previousDragDistance - angleDragDistance) > Math.PI;
if (greaterThanHalf && !sameDirection) {
// Reverse the angle
angleDragDistance = (TAU - Math.abs(angleDragDistance)) * rotationDirection;
} else {
rotationDirection = angleDragDistance > 0 ? 1 : -1;
}
draggedPie.angleDragDistance = angleDragDistance;
// Set the new angle:
piechart.data[draggedPie.index].angle = normaliseAngle(startingAngle + angleDragDistance);
// Reset Collapse
piechart.data[draggedPie.index].collapsed = draggedPie.collapsed[draggedPie.index];
// Search other angles
var shifting = true;
var collapsed = false;
var minAngle = piechart.minAngle;
var numberOfAnglesShifted = 0;
for (var i = 1; i < piechart.data.length; i += 1) {
// Index to test each slice in order
var index = mod(parseInt(draggedPie.index) + (i * rotationDirection), piechart.data.length);
// Get angle from target start to this angle
var startingAngleToNonDragged = smallestSignedAngleBetween(draggedPie.startingAngles[index], startingAngle);
// If angle is in the wrong direction then it should actually be OVER 180
if (startingAngleToNonDragged * rotationDirection < 0) {
startingAngleToNonDragged = ((startingAngleToNonDragged * rotationDirection) + TAU) * rotationDirection;
}
if (piechart.collapsing) {
// *Collapsing behaviour* when smallest angle encountered
// Reset collapse
piechart.data[index].collapsed = draggedPie.collapsed[index];
var checkForSnap = !collapsed && !piechart.data[index].collapsed;
// Snap node to collapse, and prevent going any further
if (checkForSnap && startingAngleToNonDragged > 0 && angleDragDistance > (startingAngleToNonDragged - minAngle)) {
piechart.data[draggedPie.index].angle = piechart.data[index].angle;
piechart.data[draggedPie.index].collapsed = true;
collapsed = true;
} else if (checkForSnap && startingAngleToNonDragged < 0 && angleDragDistance < (startingAngleToNonDragged + minAngle)) {
piechart.data[draggedPie.index].angle = piechart.data[index].angle;
piechart.data[index].collapsed = true;
collapsed = true;
} else {
piechart.data[index].angle = draggedPie.startingAngles[index];
}
} else {
// *Shifting behaviour* when smallest angle encountered
// Shift all other angles along
var shift = (numberOfAnglesShifted + 1) * minAngle;
if (shifting && startingAngleToNonDragged > 0 && angleDragDistance > (startingAngleToNonDragged - shift)) {
piechart.data[index].angle = normaliseAngle(draggedPie.startingAngles[index] + (angleDragDistance - startingAngleToNonDragged) + shift);
numberOfAnglesShifted += 1;
} else if (shifting && startingAngleToNonDragged < 0 && angleDragDistance < (startingAngleToNonDragged + shift)) {
piechart.data[index].angle = normaliseAngle(draggedPie.startingAngles[index] - (startingAngleToNonDragged - angleDragDistance) - shift);
numberOfAnglesShifted += 1;
} else {
shifting = false;
piechart.data[index].angle = draggedPie.startingAngles[index];
}
}
//console.log(JSON.stringify(piechart.data));
}
};
DraggablePiechart.prototype.defaults = {
onchange: function(piechart) {},
radius: 0.9,
data: [
{ angle: -2, format: { color: "#2665da", label: 'Walking'}, collapsed: false },
{ angle: -1, format: { color: "#6dd020", label: 'Programming'}, collapsed: false },
{ angle: 0, format: { color: "#f9df18", label: 'Chess'}, collapsed: false },
{ angle: 1, format: { color: "#d42a00", label: 'Eating'}, collapsed: false },
{ angle: 2, format: { color: "#e96400", label: 'Sleeping'}, collapsed: false }],
collapsing: false,
minAngle: 0.1,
drawSegment: function(context, piechart, centerX, centerY, radius, startingAngle, arcSize, format, collapsed) {
if (collapsed) { return; }
// Draw coloured segment
context.save();
var endingAngle = startingAngle + arcSize;
context.beginPath();
context.moveTo(centerX, centerY);
context.arc(centerX, centerY, radius,
startingAngle, endingAngle, false);
context.closePath();
context.fillStyle = format.color;
context.fill();
context.restore();
// Draw label on top
context.save();
context.translate(centerX, centerY);
context.rotate(startingAngle);
var fontSize = Math.floor(context.canvas.height / 25);
var dx = radius - fontSize;
var dy = centerY / 10;
context.textAlign = "right";
context.font = fontSize + "pt Helvetica";
context.fillText(format.label, dx, dy);
context.restore();
},
drawNode: function (context, piechart, x, y, centerX, centerY, hover) {
context.save();
context.translate(centerX, centerY);
context.fillStyle = '#DDDDDD';
var rad = hover ? 7 : 5;
context.beginPath();
context.arc(x, y, rad, 0, TAU, true);
context.fill();
context.stroke();
context.restore();
}
};
window.DraggablePiechart = DraggablePiechart;
/*
* Utilities + Constants
*/
var TAU = Math.PI * 2;
function degreesToRadians(degrees) {
return (degrees * Math.PI)/180;
}
function smallestSignedAngleBetween(target, source) {
return Math.atan2(Math.sin(target-source), Math.cos(target-source));
}
function mod(n, m) {
return ((n % m) + m) % m;
}
function is_touch_device() {
return 'ontouchstart' in window // works on most browsers
|| navigator.maxTouchPoints; // works on IE10/11 and Surface
}
function normaliseAngle(angle) {
return mod(angle + Math.PI, TAU) - Math.PI;
}
function polarToCartesian(angle, radius) {
return {
x: radius * Math.cos(angle),
y: radius * Math.sin(angle)
}
}
})();
(function(){
//IE9+ http://youmightnotneedjquery.com/
function ready(fn) {
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(setupPieChart);
function setupPieChart(data) {
console.log(data);
var proportions = [
{
"name": "Military",
"total": 686,
"url": "https://www.thebalance.com/department-of-defense-what-it-does-and-its-impact-3305982",
"tooltip": "Fights wars"
},
{
"name": "Health & Human Services",
"total": 70,
"url": "https://www.hhs.gov/about/index.html",
"tooltip": "Fosters advances in medicine, public health, and social services."
},
{
"name": "Education",
"total": 59.9,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_Education",
"tooltip": "Establishies policy for, administers and coordinates most federal assistance to education, collect data on US schools, and to enforce federal educational laws regarding privacy and civil rights"
},
{
"name": "Veteran Affairs",
"total": 83.1,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_Veterans_Affairs",
"tooltip": "Provides healthcare services to eligible military veterans at VA medical centers and outpatient clinics located throughout the country; several non-healthcare benefits including disability compensation, vocational rehabilitation, education assistance, home loans, and life insurance; and provides burial and memorial benefits to eligible veterans and family members."
},
{
"name": "Homeland Security",
"total": 52.7,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_Homeland_Security",
"tooltip": "Protects the United States within, at, and outside its borders. Its stated goal is to prepare for, prevent, and respond to domestic emergencies, particularly terrorism."
},
{
"name": "Energy Dept",
"total": 29.2,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_Energy",
"tooltip": "Responsibilities include the nation's nuclear weapons program, nuclear reactor production for the United States Navy, energy conservation, energy-related research, radioactive waste disposal, and domestic energy production."
},
{
"name": "Nuclear Security",
"total": 15.1,
"url": "https://en.wikipedia.org/wiki/National_Nuclear_Security_Administration",
"tooltip": "Maintains and enhances the safety, security, and effectiveness of the US nuclear weapons stockpile without nuclear explosive testing; works to reduce the global danger from weapons of mass destruction; provides the United States Navy with nuclear propulsion; and responds to nuclear and radiological emergencies in the United States and abroad."
},
{
"name": "Housing and Urban Development",
"total": 29.2,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_Housing_and_Urban_Development",
"tooltip": "Responsible for programs concerned with the Nation'shousing needs, fair housing opportunities, and improvement and development of the Nation's communities."
},
{
"name": "State Dept",
"total": 40.3,
"url": "https://en.wikipedia.org/wiki/United_States_Department_of_State",
"tooltip": "Advises the President and represents the country in international affairs and foreign policy issues."
},
{
"name": "NASA",
"total": 19.9,
"url": "https://en.wikipedia.org/wiki/NASA",
"tooltip": "Responsible for the civilian space program, as well as aeronautics and aerospace research"
},
{
"name": "All Other Agencies",
"total": 133.1,
"url": "",
"tooltip": ""
}
];
var dimensions = knuthfisheryates2(proportions);
var percent = 100/proportions.length;
for (var i = 0; i < proportions.length; i++) {
proportions[i].proportion = percent;
proportions[i].format = { color: "#2665da", label: proportions[i].name};
}
var setup = {
canvas: document.getElementById('piechart'),
radius: 0.9,
collapsing: true,
proportions: proportions,
drawSegment: drawSegmentOutlineOnly,
onchange: onPieChartChange
};
var newPie = new DraggablePiechart(setup);
function drawSegmentOutlineOnly(context, piechart, centerX, centerY, radius, startingAngle, arcSize, format, collapsed) {
if (collapsed) { return; }
// Draw segment
context.save();
var endingAngle = startingAngle + arcSize;
context.beginPath();
context.moveTo(centerX, centerY);
context.arc(centerX, centerY, radius, startingAngle, endingAngle, false);
context.closePath();
context.fillStyle = '#f5f5f5';
context.fill();
context.stroke();
context.restore();
// Draw label on top
context.save();
context.translate(centerX, centerY);
context.rotate(startingAngle);
var fontSize = Math.floor(context.canvas.height / 40);
var dx = radius - fontSize;
var dy = centerY / 10;
context.textAlign = "right";
context.font = fontSize + "pt Helvetica";
context.fillText(format.label, dx, dy);
context.restore();
}
function onPieChartChange(piechart) {
var table = document.getElementById('proportions-table');
var percentages = piechart.getAllSliceSizePercentages();
var labelsRow = '<tr>';
var propsRow = '<tr>';
for(var i = 0; i < proportions.length; i += 1) {
labelsRow += '<a href="'+proportions[i].url+'"></a><th>' + proportions[i].format.label + '</th></a>';
var v = '<var>' + percentages[i].toFixed(0) + '%</var>';
var plus = '<div id="plu-' + dimensions[i] + '" class="adjust-button" data-i="' + i + '" data-d="-1">+</div>';
var minus = '<div id="min-' + dimensions[i] + '" class="adjust-button" data-i="' + i + '" data-d="1">−</div>';
propsRow += '<td>' + v + plus + minus + '</td>';
}
labelsRow += '</tr>';
propsRow += '</tr>';
table.innerHTML = labelsRow + propsRow;
var adjust = document.getElementsByClassName("adjust-button");
function adjustClick(e) {
var i = this.getAttribute('data-i');
var d = this.getAttribute('data-d');
piechart.moveAngle(i, (d * 0.1));
}
for (i = 0; i < adjust.length; i++) {
adjust[i].addEventListener('click', adjustClick);
}
}
/*
* Array sorting algorithm
*/
function knuthfisheryates2(arr) {
var temp, j, i = arr.length;
while (--i) {
j = ~~(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
}
})();
Also see: Tab Triggers