Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
        Copyright (c) 2004-2006, The Dojo Foundation
        All Rights Reserved.

        Licensed under the Academic Free License version 2.1 or above OR the
        modified BSD license. For more information on Dojo licensing, see:

                http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.svg.Chart");
dojo.require("dojo.widget.HtmlWidget");
dojo.require("dojo.widget.Chart");
dojo.require("dojo.html.layout");
dojo.require("dojo.math");
dojo.require("dojo.svg");
dojo.require("dojo.gfx.color");
dojo.require("dojo.json");
dojo.widget.defineWidget("dojo.widget.svg.Chart", [dojo.widget.HtmlWidget, dojo.widget.Chart], function () {
        this.templatePath = null;
        this.templateCssPath = null;
        this._isInitialize = false;
        this.hasData = false;
        this.vectorNode = null;
        this.plotArea = null;
        this.dataGroup = null;
        this.axisGroup = null;
        this.properties = {height:0, width:0, defaultWidth:600, defaultHeight:400, plotType:null, padding:{top:10, bottom:2, left:60, right:30}, axes:{x:{plotAt:0, label:"", unitLabel:"", unitType:Number, nUnitsToShow:10, range:{min:0, max:200}}, y:{plotAt:0, label:"", unitLabel:"", unitType:Number, nUnitsToShow:10, range:{min:0, max:200}}}};
}, {parseProperties:function (node) {
        var bRangeX = false;
        var bRangeY = false;
        if (node.getAttribute("width")) {
                this.properties.width = node.getAttribute("width");
        }
        if (node.getAttribute("height")) {
                this.properties.height = node.getAttribute("height");
        }
        if (node.getAttribute("plotType")) {
                this.properties.plotType = node.getAttribute("plotType");
        }
        if (node.getAttribute("padding")) {
                if (node.getAttribute("padding").indexOf(",") > -1) {
                        var p = node.getAttribute("padding").split(",");
                } else {
                        var p = node.getAttribute("padding").split(" ");
                }
                if (p.length == 1) {
                        var pad = parseFloat(p[0]);
                        this.properties.padding.top = pad;
                        this.properties.padding.right = pad;
                        this.properties.padding.bottom = pad;
                        this.properties.padding.left = pad;
                } else {
                        if (p.length == 2) {
                                var padV = parseFloat(p[0]);
                                var padH = parseFloat(p[1]);
                                this.properties.padding.top = padV;
                                this.properties.padding.right = padH;
                                this.properties.padding.bottom = padV;
                                this.properties.padding.left = padH;
                        } else {
                                if (p.length == 4) {
                                        this.properties.padding.top = parseFloat(p[0]);
                                        this.properties.padding.right = parseFloat(p[1]);
                                        this.properties.padding.bottom = parseFloat(p[2]);
                                        this.properties.padding.left = parseFloat(p[3]);
                                }
                        }
                }
        }
        if (node.getAttribute("rangeX")) {
                var p = node.getAttribute("rangeX");
                if (p.indexOf(",") > -1) {
                        p = p.split(",");
                } else {
                        p = p.split(" ");
                }
                this.properties.axes.x.range.min = parseFloat(p[0]);
                this.properties.axes.x.range.max = parseFloat(p[1]);
                bRangeX = true;
        }
        if (node.getAttribute("rangeY")) {
                var p = node.getAttribute("rangeY");
                if (p.indexOf(",") > -1) {
                        p = p.split(",");
                } else {
                        p = p.split(" ");
                }
                this.properties.axes.y.range.min = parseFloat(p[0]);
                this.properties.axes.y.range.max = parseFloat(p[1]);
                bRangeY = true;
        }
        return {rangeX:bRangeX, rangeY:bRangeY};
}, setAxesPlot:function (table) {
        if (table.getAttribute("axisAt")) {
                var p = table.getAttribute("axisAt");
                if (p.indexOf(",") > -1) {
                        p = p.split(",");
                } else {
                        p = p.split(" ");
                }
                if (!isNaN(parseFloat(p[0]))) {
                        this.properties.axes.x.plotAt = parseFloat(p[0]);
                } else {
                        if (p[0].toLowerCase() == "ymin") {
                                this.properties.axes.x.plotAt = this.properties.axes.y.range.min;
                        } else {
                                if (p[0].toLowerCase() == "ymax") {
                                        this.properties.axes.x.plotAt = this.properties.axes.y.range.max;
                                }
                        }
                }
                if (!isNaN(parseFloat(p[1]))) {
                        this.properties.axes.y.plotAt = parseFloat(p[1]);
                } else {
                        if (p[1].toLowerCase() == "xmin") {
                                this.properties.axes.y.plotAt = this.properties.axes.x.range.min;
                        } else {
                                if (p[1].toLowerCase() == "xmax") {
                                        this.properties.axes.y.plotAt = this.properties.axes.x.range.max;
                                }
                        }
                }
        } else {
                this.properties.axes.x.plotAt = this.properties.axes.y.range.min;
                this.properties.axes.y.plotAt = this.properties.axes.x.range.min;
        }
}, drawVectorNode:function () {
        dojo.svg.g.suspend();
        if (this.vectorNode) {
                this.destroy();
        }
        this.vectorNode = document.createElementNS(dojo.svg.xmlns.svg, "svg");
        this.vectorNode.setAttribute("width", this.properties.width);
        this.vectorNode.setAttribute("height", this.properties.height);
        dojo.svg.g.resume();
}, drawPlotArea:function () {
        dojo.svg.g.suspend();
        if (this.plotArea) {
                this.plotArea.parentNode.removeChild(this.plotArea);
                this.plotArea = null;
        }
        var defs = document.createElementNS(dojo.svg.xmlns.svg, "defs");
        var clip = document.createElementNS(dojo.svg.xmlns.svg, "clipPath");
        clip.setAttribute("id", "plotClip" + this.widgetId);
        var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");
        rect.setAttribute("x", this.properties.padding.left);
        rect.setAttribute("y", this.properties.padding.top);
        rect.setAttribute("width", this.properties.width - this.properties.padding.left - this.properties.padding.right);
        rect.setAttribute("height", this.properties.height - this.properties.padding.top - this.properties.padding.bottom);
        clip.appendChild(rect);
        defs.appendChild(clip);
        this.vectorNode.appendChild(defs);
        this.plotArea = document.createElementNS(dojo.svg.xmlns.svg, "g");
        this.vectorNode.appendChild(this.plotArea);
        var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");
        rect.setAttribute("x", this.properties.padding.left);
        rect.setAttribute("y", this.properties.padding.top);
        rect.setAttribute("width", this.properties.width - this.properties.padding.left - this.properties.padding.right);
        rect.setAttribute("height", this.properties.height - this.properties.padding.top - this.properties.padding.bottom);
        rect.setAttribute("fill", "#fff");
        this.plotArea.appendChild(rect);
        dojo.svg.g.resume();
}, drawDataGroup:function () {
        dojo.svg.g.suspend();
        if (this.dataGroup) {
                this.dataGroup.parentNode.removeChild(this.dataGroup);
                this.dataGroup = null;
        }
        this.dataGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
        this.dataGroup.setAttribute("style", "clip-path:url(#plotClip" + this.widgetId + ");");
        this.plotArea.appendChild(this.dataGroup);
        dojo.svg.g.resume();
}, drawAxes:function () {
        dojo.svg.g.suspend();
        if (this.axisGroup) {
                this.axisGroup.parentNode.removeChild(this.axisGroup);
                this.axisGroup = null;
        }
        this.axisGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
        this.plotArea.appendChild(this.axisGroup);
        var stroke = 1;
        var line = document.createElementNS(dojo.svg.xmlns.svg, "line");
        var y = dojo.widget.svg.Chart.Plotter.getY(this.properties.axes.x.plotAt, this);
        line.setAttribute("y1", y);
        line.setAttribute("y2", y);
        line.setAttribute("x1", this.properties.padding.left - stroke);
        line.setAttribute("x2", this.properties.width - this.properties.padding.right);
        line.setAttribute("style", "stroke:#000;stroke-width:" + stroke + ";");
        this.axisGroup.appendChild(line);
        var textSize = 10;
        var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
        text.setAttribute("x", this.properties.padding.left);
        text.setAttribute("y", this.properties.height - this.properties.padding.bottom + textSize + 2);
        text.setAttribute("style", "text-anchor:middle;font-size:" + textSize + "px;fill:#000;");
        text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.min), 2)));
        this.axisGroup.appendChild(text);
        var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
        text.setAttribute("x", this.properties.width - this.properties.padding.right - (textSize / 2));
        text.setAttribute("y", this.properties.height - this.properties.padding.bottom + textSize + 2);
        text.setAttribute("style", "text-anchor:middle;font-size:" + textSize + "px;fill:#000;");
        text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.max), 2)));
        this.axisGroup.appendChild(text);
        var line = document.createElementNS(dojo.svg.xmlns.svg, "line");
        var x = dojo.widget.svg.Chart.Plotter.getX(this.properties.axes.y.plotAt, this);
        line.setAttribute("x1", x);
        line.setAttribute("x2", x);
        line.setAttribute("y1", this.properties.padding.top);
        line.setAttribute("y2", this.properties.height - this.properties.padding.bottom);
        line.setAttribute("style", "stroke:#000;stroke-width:" + stroke + ";");
        this.axisGroup.appendChild(line);
        var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
        text.setAttribute("x", this.properties.padding.left - 4);
        text.setAttribute("y", this.properties.height - this.properties.padding.bottom);
        text.setAttribute("style", "text-anchor:end;font-size:" + textSize + "px;fill:#000;");
        text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.min), 2)));
        this.axisGroup.appendChild(text);
        var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
        text.setAttribute("x", this.properties.padding.left - 4);
        text.setAttribute("y", this.properties.padding.top + (textSize / 2));
        text.setAttribute("style", "text-anchor:end;font-size:" + textSize + "px;fill:#000;");
        text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.max), 2)));
        this.axisGroup.appendChild(text);
        dojo.svg.g.resume();
}, init:function () {
        if (!this.properties.width || !this.properties.height) {
                var box = dojo.html.getContentBox(this.domNode);
                if (!this.properties.width) {
                        this.properties.width = (box.width < 32) ? this.properties.defaultWidth : box.width;
                }
                if (!this.properties.height) {
                        this.properties.height = (box.height < 32) ? this.properties.defaultHeight : box.height;
                }
        }
        this.drawVectorNode();
        this.drawPlotArea();
        this.drawDataGroup();
        this.drawAxes();
        this.domNode.appendChild(this.vectorNode);
        this.assignColors();
        this._isInitialized = true;
}, destroy:function () {
        while (this.domNode.childNodes.length > 0) {
                this.domNode.removeChild(this.domNode.childNodes.item(0));
        }
        this.vectorNode = this.plotArea = this.dataGroup = this.axisGroup = null;
}, render:function () {
        dojo.svg.g.suspend();
        if (this.dataGroup) {
                while (this.dataGroup.childNodes.length > 0) {
                        this.dataGroup.removeChild(this.dataGroup.childNodes.item(0));
                }
        } else {
                this.init();
        }
        for (var i = 0; i < this.series.length; i++) {
                dojo.widget.svg.Chart.Plotter.plot(this.series[i], this);
        }
        dojo.svg.g.resume();
}, postCreate:function () {
        var table = this.domNode.getElementsByTagName("table")[0];
        if (table) {
                var ranges = this.parseProperties(table);
                var bRangeX = false;
                var bRangeY = false;
                var axisValues = this.parseData(table);
                if (!bRangeX) {
                        this.properties.axes.x.range = {min:axisValues.x.min, max:axisValues.x.max};
                }
                if (!bRangeY) {
                        this.properties.axes.y.range = {min:axisValues.y.min, max:axisValues.y.max};
                }
                this.setAxesPlot(table);
                this.domNode.removeChild(table);
        }
        if (this.series.length > 0) {
                this.render();
        }
}});
dojo.widget.svg.Chart.Plotter = new function () {
        var self = this;
        var plotters = {};
        var types = dojo.widget.Chart.PlotTypes;
        this.getX = function (value, chart) {
                var v = parseFloat(value);
                var min = chart.properties.axes.x.range.min;
                var max = chart.properties.axes.x.range.max;
                var ofst = 0 - min;
                min += ofst;
                max += ofst;
                v += ofst;
                var xmin = chart.properties.padding.left;
                var xmax = chart.properties.width - chart.properties.padding.right;
                var x = (v * ((xmax - xmin) / max)) + xmin;
                return x;
        };
        this.getY = function (value, chart) {
                var v = parseFloat(value);
                var max = chart.properties.axes.y.range.max;
                var min = chart.properties.axes.y.range.min;
                var ofst = 0;
                if (min < 0) {
                        ofst += Math.abs(min);
                }
                min += ofst;
                max += ofst;
                v += ofst;
                var ymin = chart.properties.height - chart.properties.padding.bottom;
                var ymax = chart.properties.padding.top;
                var y = (((ymin - ymax) / (max - min)) * (max - v)) + ymax;
                return y;
        };
        this.addPlotter = function (name, func) {
                plotters[name] = func;
        };
        this.plot = function (series, chart) {
                if (series.values.length == 0) {
                        return;
                }
                if (series.plotType && plotters[series.plotType]) {
                        return plotters[series.plotType](series, chart);
                } else {
                        if (chart.plotType && plotters[chart.plotType]) {
                                return plotters[chart.plotType](series, chart);
                        }
                }
        };
        plotters["bar"] = function (series, chart) {
                var space = 1;
                var lastW = 0;
                for (var i = 0; i < series.values.length; i++) {
                        var x = self.getX(series.values[i].x, chart);
                        var w;
                        if (i == series.values.length - 1) {
                                w = lastW;
                        } else {
                                w = self.getX(series.values[i + 1].x, chart) - x - space;
                                lastW = w;
                        }
                        x -= (w / 2);
                        var yA = self.getY(chart.properties.axes.x.plotAt, chart);
                        var y = self.getY(series.values[i].value, chart);
                        var h = Math.abs(yA - y);
                        if (parseFloat(series.values[i].value) < chart.properties.axes.x.plotAt) {
                                var oy = yA;
                                yA = y;
                                y = oy;
                        }
                        var bar = document.createElementNS(dojo.svg.xmlns.svg, "rect");
                        bar.setAttribute("fill", series.color);
                        bar.setAttribute("title", series.label + ": " + series.values[i].value);
                        bar.setAttribute("stroke-width", "0");
                        bar.setAttribute("x", x);
                        bar.setAttribute("y", y);
                        bar.setAttribute("width", w);
                        bar.setAttribute("height", h);
                        bar.setAttribute("fill-opacity", "0.9");
                        chart.dataGroup.appendChild(bar);
                }
        };
        plotters["line"] = function (series, chart) {
                var tension = 1.5;
                var line = document.createElementNS(dojo.svg.xmlns.svg, "path");
                line.setAttribute("fill", "none");
                line.setAttribute("stroke", series.color);
                line.setAttribute("stroke-width", "2");
                line.setAttribute("stroke-opacity", "0.85");
                line.setAttribute("title", series.label);
                chart.dataGroup.appendChild(line);
                var path = [];
                for (var i = 0; i < series.values.length; i++) {
                        var x = self.getX(series.values[i].x, chart);
                        var y = self.getY(series.values[i].value, chart);
                        var dx = chart.properties.padding.left + 1;
                        var dy = chart.properties.height - chart.properties.padding.bottom;
                        if (i > 0) {
                                dx = x - self.getX(series.values[i - 1].x, chart);
                                dy = self.getY(series.values[i - 1].value, chart);
                        }
                        if (i == 0) {
                                path.push("M");
                        } else {
                                path.push("C");
                                var cx = x - (tension - 1) * (dx / tension);
                                path.push(cx + "," + dy);
                                cx = x - (dx / tension);
                                path.push(cx + "," + y);
                        }
                        path.push(x + "," + y);
                }
                line.setAttribute("d", path.join(" "));
        };
        plotters["area"] = function (series, chart) {
                var tension = 1.5;
                var line = document.createElementNS(dojo.svg.xmlns.svg, "path");
                line.setAttribute("fill", series.color);
                line.setAttribute("fill-opacity", "0.4");
                line.setAttribute("stroke", series.color);
                line.setAttribute("stroke-width", "1");
                line.setAttribute("stroke-opacity", "0.8");
                line.setAttribute("title", series.label);
                chart.dataGroup.appendChild(line);
                var path = [];
                for (var i = 0; i < series.values.length; i++) {
                        var x = self.getX(series.values[i].x, chart);
                        var y = self.getY(series.values[i].value, chart);
                        var dx = chart.properties.padding.left + 1;
                        var dy = chart.properties.height - chart.properties.padding.bottom;
                        if (i > 0) {
                                dx = x - self.getX(series.values[i - 1].x, chart);
                                dy = self.getY(series.values[i - 1].value, chart);
                        }
                        if (i == 0) {
                                path.push("M");
                        } else {
                                path.push("C");
                                var cx = x - (tension - 1) * (dx / tension);
                                path.push(cx + "," + dy);
                                cx = x - (dx / tension);
                                path.push(cx + "," + y);
                        }
                        path.push(x + "," + y);
                }
                path.push("L");
                path.push(x + "," + self.getY(0, chart));
                path.push("L");
                path.push(self.getX(0, chart) + "," + self.getY(0, chart));
                path.push("Z");
                line.setAttribute("d", path.join(" "));
        }, plotters["scatter"] = function (series, chart) {
                var r = 7;
                for (var i = 0; i < series.values.length; i++) {
                        var x = self.getX(series.values[i].x, chart);
                        var y = self.getY(series.values[i].value, chart);
                        var point = document.createElementNS(dojo.svg.xmlns.svg, "path");
                        point.setAttribute("fill", series.color);
                        point.setAttribute("stroke-width", "0");
                        point.setAttribute("title", series.label + ": " + series.values[i].value);
                        point.setAttribute("d", "M " + x + "," + (y - r) + " " + "Q " + x + "," + y + " " + (x + r) + "," + y + " " + "Q " + x + "," + y + " " + x + "," + (y + r) + " " + "Q " + x + "," + y + " " + (x - r) + "," + y + " " + "Q " + x + "," + y + " " + x + "," + (y - r) + " " + "Z");
                        chart.dataGroup.appendChild(point);
                }
        };
        plotters["bubble"] = function (series, chart) {
                var minR = 1;
                var min = chart.properties.axes.x.range.min;
                var max = chart.properties.axes.x.range.max;
                var ofst = 0 - min;
                min += ofst;
                max += ofst;
                var xmin = chart.properties.padding.left;
                var xmax = chart.properties.width - chart.properties.padding.right;
                var factor = (max - min) / (xmax - xmin) * 25;
                for (var i = 0; i < series.values.length; i++) {
                        var size = series.values[i].size;
                        if (isNaN(parseFloat(size))) {
                                size = minR;
                        }
                        var point = document.createElementNS(dojo.svg.xmlns.svg, "circle");
                        point.setAttribute("stroke-width", 0);
                        point.setAttribute("fill", series.color);
                        point.setAttribute("fill-opacity", "0.8");
                        point.setAttribute("r", (parseFloat(size) * factor) / 2);
                        point.setAttribute("cx", self.getX(series.values[i].x, chart));
                        point.setAttribute("cy", self.getY(series.values[i].value, chart));
                        point.setAttribute("title", series.label + ": " + series.values[i].value + " (" + size + ")");
                        chart.dataGroup.appendChild(point);
                }
        };
}();