Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

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.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

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.

+ add another resource

Packages

Add Packages

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.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <!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>
              
            
!

CSS

              
                
* {
    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;
}
              
            
!

JS

              
                /**
 * 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">&#43;</div>';
                var minus = '<div id="min-' + dimensions[i] + '" class="adjust-button" data-i="' + i + '" data-d="1">&#8722;</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;
        }
    }

})();




              
            
!
999px

Console