ui/infographic.js


export class Infographic {

    /**
    * This constructor creates the chart itself and surrounding HTML such as dropdown buttons.
    * @param {string} chart_id - The id of the chart.
    * @param {string} chartType - The type of the chart, i.e line, pie or donut.
    * @param {string} title - The title of the infographic.
    * @param {int} width - The width of a chart.
    * @param {int} height - The height of a chart.
    * @param {object} data - The data to be inserted into a given chart.
    */
    constructor(chart_id, chartType, title, width, height, data) {
        if (constructor === 'Infographic') {
            throw new Error("Cannot instantiate abstract class \"Infographic\".");
        }

        this.context = null;
        this.chart = null;
        this.updateAnimation = false;

        this.typename = chartType;
        this.data = data;
        this.dropdowns = [];
        this.dindex = 0;

        /** References to HTML objects */
        this.dropdown = null;
        this.container = null;

        /** Canvas generation */
        if (chart_id === "donut"){
          var allCharts = document.getElementById("donut_chart");
        } else if (chart_id === "pie"){
          var allCharts = document.getElementById("pie_chart");
        } else if (chart_id === "line"){
          var allCharts = document.getElementById("line_chart");
        } else {
          var allCharts = document.getElementById("overflow_charts");
        }

        this.container = document.createElement("div");
        this.container.class = "container";
        this.container.style = "max-width: 800px; margin: 50px auto;";

        this.container.innerHTML =
        "<div>\n" +
        "<h2 style=\"font-size: 24pt\">" + title + "</h2>\n" +
        "<canvas id=\"" + chart_id + "\" width=\"" + width + "\" height=\"" + height + "\"></canvas>" +
        "</div>";

        /** Dropdown generation for charts with more than one option. */
        if (this.data.length > 1) {
            this.dropdown = document.createElement("label");

            this.dropdown.innerHTML = "Choose data type:\n";
            allCharts.appendChild(this.dropdown);

            var selector = document.createElement("select");

            // TODO: are these needed? (don't think they need to be these names, might not be needed at all, originally for testing - Tom)
            selector.class = "charts";
            selector.name = "ice-cream";
            selector.id = "chartSelect";

            for (var i = 0; i < this.data.length; i++) {
                selector.innerHTML = selector.innerHTML + "<option value=\"" + this.data[i].name + "\">" + this.data[i].name + "</option>\n";
            }

            var infographic = this;
            selector.onchange = function(change_event) {
                var selected = change_event.target.value;

                for (var i = 0; i < infographic.data.length; i++) {
                    if (selected === infographic.data[i].name){
                        if (infographic.chart != null) {
                            infographic.chart.destroy();
                        }
                        // Populate the chart
                        infographic.populate(i);
                        break;
                    }
                }

            };

            this.dropdown.appendChild(selector);
        }

        allCharts.appendChild(this.container);
        this.context = document.getElementById(chart_id);

        /** Populate with default data */
        this.populate(this.dindex);
    }

    /** This function populates the chart with the specified option index
    * @param {int} index - The data type option index of the chart.
    */
    populate(index) {
        if (this.data.length > 0 && this.data[index].datasets != null && this.data[index].datasets.length > 0) {
            this.dindex = index;
            this.stopDrawing();
            this.chart = new Chart(this.context, {
                type: this.typename,
                data: {
                    labels: this.data[index].labels,
                    datasets: this.data[index].datasets
                },
                options: this.data[index].options
            });
        } else {
            this.startDrawing();
        }
    }

    /** This function starts drawing the loading animation */
    startDrawing() {
        // TODO: Check if data is being loaded. If so, show spinning wheel; otherwise show "No data available".
        var canvas = this.context.getContext("2d");
        canvas.save();
        this.updateAnimation = true;
        var infographic = this;
        window.requestAnimationFrame(function () { infographic.drawLoading(); });
    }

    /** Stop drawing the loading animation */
    stopDrawing() {
        this.updateAnimation = false;
    }

    /** Draw a frame of the loading animation */
    drawLoading() {
        var canvas = this.context.getContext("2d");
        var halfDim = { w: this.context.width / 2, h: this.context.height / 2 };

        canvas.globalCompositeOperation = 'destination-over';
        canvas.clearRect(0, 0, halfDim.w * 2, halfDim.h * 2);
        canvas.fillStyle = 'rgba(0, 255, 255, 1)';
        canvas.strokeStyle = 'rgba(200, 200, 200, 1)';
        canvas.save();

        var loadingSpinSpeed = 1;
        var loadingDotRadius = 8;
        var loadingWheelRadius = 32;

        /** Loading wheel */
        var time = (new Date()).getMilliseconds();
        var deltaTime = (time - this.lastTime) / 1000;
        for (var i = 0, count = 1; i < count; i++) {
            var trans = {
                x: halfDim.w - loadingWheelRadius - loadingDotRadius,
                y: halfDim.h - loadingWheelRadius - loadingDotRadius
            };


            canvas.translate(trans.x, trans.y);
            canvas.rotate((2 * Math.PI) * deltaTime * loadingSpinSpeed);

            /** Draw loading dot */
            canvas.beginPath();
            canvas.arc(loadingWheelRadius, 0, loadingDotRadius, 0, 2 * Math.PI);
            canvas.fill();
            canvas.translate(-trans.x, -trans.y);


            /** Draw wheel */
            canvas.beginPath();
            canvas.arc(trans.x, trans.y, loadingWheelRadius, 0, 2 * Math.PI);
            canvas.lineWidth = loadingDotRadius * 2;
            canvas.stroke();
        }
        this.lastTime = time;

        if (this.updateAnimation) {
            var infographic = this;
            window.requestAnimationFrame(function () { infographic.drawLoading(); });
        } else {
            canvas.restore();
        }
    }
    /** Update the infographic with new data */
    update(data) {
        this.data = data;
        this.populate(this.dindex);
    }

}