this.options = {
"drawBackground": true,
"backgroundColor": Color.whiteColor(),
- "padding": {left: 30, right: 30, top: 5, bottom: 10},
"colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),
"strokeColor": Color.whiteColor(),
"strokeColorTransform": "asStrokeColor",
this.isFirstRender = true;
this.area = {
- x: this.options.padding.left,
- y: this.options.padding.top,
- w: this.width - this.options.padding.left - this.options.padding.right,
- h: this.height - this.options.padding.top - this.options.padding.bottom
+ x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize,
+ y: 0
};
+ this.area.w = this.width - this.area.x - this.options.rightGap;
+ this.area.h = this.height - this.options.axisLabelFontSize -
+ 2 * this.options.axisTickSize;
MochiKit.DOM.updateNodeAttributes(this.container,
{"style":{ "position": "relative", "width": this.width + "px"}});
if (this.options.drawBackground)
this._renderBackground();
- if (this.layout.style == "bar") {
- this._renderBarChart();
- this._renderBarAxis();
- }
- else if (this.layout.style == "pie") {
- this._renderPieChart();
- this._renderPieAxis();
- }
- else if (this.layout.style == "line") {
+ if (this.layout.style == "line") {
this._renderLineChart();
this._renderLineAxis();
}
};
-PlotKit.CanvasRenderer.prototype._renderBarChartWrap = function(data, plotFunc) {
- var context = this.element.getContext("2d");
- var colorCount = this.options.colorScheme.length;
- var colorScheme = this.options.colorScheme;
- var setNames = MochiKit.Base.keys(this.layout.datasets);
- var setCount = setNames.length;
-
- for (var i = 0; i < setCount; i++) {
- var setName = setNames[i];
- var color = colorScheme[i%colorCount];
- context.save();
- context.fillStyle = color.toRGBString();
- if (this.options.strokeColor)
- context.strokeStyle = this.options.strokeColor.toRGBString();
- else if (this.options.strokeColorTransform)
- context.strokeStyle = color[this.options.strokeColorTransform]().toRGBString();
-
- context.lineWidth = this.options.strokeWidth;
- var forEachFunc = function(obj) {
- if (obj.name == setName)
- plotFunc(context, obj);
- };
-
- MochiKit.Iter.forEach(data, bind(forEachFunc, this));
- context.restore();
- }
-};
-
-PlotKit.CanvasRenderer.prototype._renderBarChart = function() {
- var bind = MochiKit.Base.bind;
-
- var drawRect = function(context, bar) {
- var x = this.area.w * bar.x + this.area.x;
- var y = this.area.h * bar.y + this.area.y;
- var w = this.area.w * bar.w;
- var h = this.area.h * bar.h;
- if ((w < 1) || (h < 1))
- return;
- if (this.options.shouldFill)
- context.fillRect(x, y, w, h);
- if (this.options.shouldStroke)
- context.strokeRect(x, y, w, h);
- };
- this._renderBarChartWrap(this.layout.bars, bind(drawRect, this));
-};
-
PlotKit.CanvasRenderer.prototype._renderLineChart = function() {
var context = this.element.getContext("2d");
var colorCount = this.options.colorScheme.length;
}
};
-PlotKit.CanvasRenderer.prototype._renderPieChart = function() {
- var context = this.element.getContext("2d");
- var colorCount = this.options.colorScheme.length;
- var slices = this.layout.slices;
-
- var centerx = this.area.x + this.area.w * 0.5;
- var centery = this.area.y + this.area.h * 0.5;
- var radius = Math.min(this.area.w * this.options.pieRadius,
- this.area.h * this.options.pieRadius);
-
- if (this.isIE) {
- centerx = parseInt(centerx);
- centery = parseInt(centery);
- radius = parseInt(radius);
- }
-
-
- // NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1
- // so we have to subtract 90 degrees to make it start at y = 1, x = 0
-
- for (var i = 0; i < slices.length; i++) {
- var color = this.options.colorScheme[i%colorCount];
- context.save();
- context.fillStyle = color.toRGBString();
-
- var makePath = function() {
- context.beginPath();
- context.moveTo(centerx, centery);
- context.arc(centerx, centery, radius,
- slices[i].startAngle - Math.PI/2,
- slices[i].endAngle - Math.PI/2,
- false);
- context.lineTo(centerx, centery);
- context.closePath();
- };
-
- if (Math.abs(slices[i].startAngle - slices[i].endAngle) > 0.001) {
- if (this.options.shouldFill) {
- makePath();
- context.fill();
- }
-
- if (this.options.shouldStroke) {
- makePath();
- context.lineWidth = this.options.strokeWidth;
- if (this.options.strokeColor)
- context.strokeStyle = this.options.strokeColor.toRGBString();
- else if (this.options.strokeColorTransform)
- context.strokeStyle = color[this.options.strokeColorTransform]().toRGBString();
- context.stroke();
- }
- }
- context.restore();
- }
-};
-
-PlotKit.CanvasRenderer.prototype._renderBarAxis = function() {
- this._renderAxis();
-}
PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
this._renderAxis();
context.stroke();
var label = DIV(labelStyle, tick[1]);
- label.style.top = (y - this.options.axisLabelFontSize) + "px";
- label.style.left = (x - this.options.padding.left - this.options.axisTickSize) + "px";
+ var top = (y - this.options.axisLabelFontSize / 2);
+ if (top < 0) top = 0;
+
+ if (top + this.options.axisLabelFontSize + 3 > this.height) {
+ label.style.bottom = "0px";
+ } else {
+ label.style.top = top + "px";
+ }
+ label.style.left = "0px";
label.style.textAlign = "right";
- label.style.width = (this.options.padding.left - this.options.axisTickSize * 2) + "px";
+ label.style.width = this.options.yAxisLabelWidth + "px";
MochiKit.DOM.appendChildNodes(this.container, label);
this.ylabels.push(label);
};
MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
+
+ // The lowest tick on the y-axis often overlaps with the leftmost
+ // tick on the x-axis. Shift the bottom tick up a little bit to
+ // compensate if necessary.
+ var bottomTick = this.ylabels[0];
+ var fontSize = this.options.axisLabelFontSize;
+ var bottom = parseInt(bottomTick.style.top) + fontSize;
+ if (bottom > this.height - fontSize) {
+ bottomTick.style.top = (parseInt(bottomTick.style.top) -
+ fontSize / 2) + "px";
+ }
}
context.beginPath();
context.stroke();
var label = DIV(labelStyle, tick[1]);
- label.style.top = (y + this.options.axisTickSize) + "px";
- label.style.left = (x - this.options.axisLabelWidth/2) + "px";
label.style.textAlign = "center";
- label.style.width = this.options.axisLabelWidth + "px";
+ label.style.bottom = "0px";
+
+ var left = (x - this.options.axisLabelWidth/2);
+ if (left + this.options.axisLabelWidth > this.width) {
+ left = this.width - this.options.xAxisLabelWidth;
+ label.style.textAlign = "right";
+ }
+ if (left < 0) {
+ left = 0;
+ label.style.textAlign = "left";
+ }
+
+ label.style.left = left + "px";
+ label.style.width = this.options.xAxisLabelWidth + "px";
MochiKit.DOM.appendChildNodes(this.container, label);
this.xlabels.push(label);
};
};
-PlotKit.CanvasRenderer.prototype._renderPieAxis = function() {
- if (!this.options.drawXAxis)
- return;
-
- if (this.layout.xticks) {
- // make a lookup dict for x->slice values
- var lookup = new Array();
- for (var i = 0; i < this.layout.slices.length; i++) {
- lookup[this.layout.slices[i].xval] = this.layout.slices[i];
- }
-
- var centerx = this.area.x + this.area.w * 0.5;
- var centery = this.area.y + this.area.h * 0.5;
- var radius = Math.min(this.area.w * this.options.pieRadius,
- this.area.h * this.options.pieRadius);
- var labelWidth = this.options.axisLabelWidth;
-
- for (var i = 0; i < this.layout.xticks.length; i++) {
- var slice = lookup[this.layout.xticks[i][0]];
- if (MochiKit.Base.isUndefinedOrNull(slice))
- continue;
-
-
- var angle = (slice.startAngle + slice.endAngle)/2;
- // normalize the angle
- var normalisedAngle = angle;
- if (normalisedAngle > Math.PI * 2)
- normalisedAngle = normalisedAngle - Math.PI * 2;
- else if (normalisedAngle < 0)
- normalisedAngle = normalisedAngle + Math.PI * 2;
-
- var labelx = centerx + Math.sin(normalisedAngle) * (radius + 10);
- var labely = centery - Math.cos(normalisedAngle) * (radius + 10);
-
- var attrib = {"position": "absolute",
- "zIndex": 11,
- "width": labelWidth + "px",
- "fontSize": this.options.axisLabelFontSize + "px",
- "overflow": "hidden",
- "color": this.options.axisLabelColor.toHexString()
- };
-
- if (normalisedAngle <= Math.PI * 0.5) {
- // text on top and align left
- attrib["textAlign"] = "left";
- attrib["verticalAlign"] = "top";
- attrib["left"] = labelx + "px";
- attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
- }
- else if ((normalisedAngle > Math.PI * 0.5) && (normalisedAngle <= Math.PI)) {
- // text on bottom and align left
- attrib["textAlign"] = "left";
- attrib["verticalAlign"] = "bottom";
- attrib["left"] = labelx + "px";
- attrib["top"] = labely + "px";
-
- }
- else if ((normalisedAngle > Math.PI) && (normalisedAngle <= Math.PI*1.5)) {
- // text on bottom and align right
- attrib["textAlign"] = "right";
- attrib["verticalAlign"] = "bottom";
- attrib["left"] = (labelx - labelWidth) + "px";
- attrib["top"] = labely + "px";
- }
- else {
- // text on top and align right
- attrib["textAlign"] = "right";
- attrib["verticalAlign"] = "bottom";
- attrib["left"] = (labelx - labelWidth) + "px";
- attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
- }
-
- var label = DIV({'style': attrib}, this.layout.xticks[i][1]);
- this.xlabels.push(label);
- MochiKit.DOM.appendChildNodes(this.container, label);
- }
-
- }
-};
-
PlotKit.CanvasRenderer.prototype._renderBackground = function() {
var context = this.element.getContext("2d");
context.save();