801 lines
29 KiB
JavaScript
801 lines
29 KiB
JavaScript
//>>built
|
|
define("dojox/charting/axis2d/Default", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/sniff", "dojo/_base/declare",
|
|
"dojo/_base/connect", "dojo/_base/html", "dojo/dom-geometry", "./Invisible",
|
|
"../scaler/common", "../scaler/linear", "./common", "dojox/gfx", "dojox/lang/utils"],
|
|
function(lang, arr, has, declare, connect, html, domGeom, Invisible, scommon,
|
|
lin, acommon, g, du){
|
|
|
|
/*=====
|
|
dojox.charting.axis2d.__AxisCtorArgs = function(
|
|
vertical, fixUpper, fixLower, natural, leftBottom,
|
|
includeZero, fixed, majorLabels, minorTicks, minorLabels, microTicks, htmlLabels,
|
|
min, max, from, to, majorTickStep, minorTickStep, microTickStep,
|
|
labels, labelFunc, maxLabelSize,
|
|
stroke, majorTick, minorTick, microTick, tick,
|
|
font, fontColor
|
|
){
|
|
// summary:
|
|
// Optional arguments used in the definition of an axis.
|
|
//
|
|
// vertical: Boolean?
|
|
// A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal).
|
|
// fixUpper: String?
|
|
// Align the greatest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none".
|
|
// fixLower: String?
|
|
// Align the smallest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none".
|
|
// natural: Boolean?
|
|
// Ensure tick marks are made on "natural" numbers. Defaults to false.
|
|
// leftBottom: Boolean?
|
|
// The position of a vertical axis; if true, will be placed against the left-bottom corner of the chart. Defaults to true.
|
|
// includeZero: Boolean?
|
|
// Include 0 on the axis rendering. Default is false.
|
|
// fixed: Boolean?
|
|
// Force all axis labels to be fixed numbers. Default is true.
|
|
// majorLabels: Boolean?
|
|
// Flag to draw all labels at major ticks. Default is true.
|
|
// minorTicks: Boolean?
|
|
// Flag to draw minor ticks on an axis. Default is true.
|
|
// minorLabels: Boolean?
|
|
// Flag to draw labels on minor ticks. Default is true.
|
|
// microTicks: Boolean?
|
|
// Flag to draw micro ticks on an axis. Default is false.
|
|
// htmlLabels: Boolean?
|
|
// Flag to use HTML (as opposed to the native vector graphics engine) to draw labels. Default is true.
|
|
// min: Number?
|
|
// The smallest value on an axis. Default is 0.
|
|
// max: Number?
|
|
// The largest value on an axis. Default is 1.
|
|
// from: Number?
|
|
// Force the chart to render data visible from this value. Default is 0.
|
|
// to: Number?
|
|
// Force the chart to render data visible to this value. Default is 1.
|
|
// majorTickStep: Number?
|
|
// The amount to skip before a major tick is drawn. Default is 4.
|
|
// minorTickStep: Number?
|
|
// The amount to skip before a minor tick is drawn. Default is 2.
|
|
// microTickStep: Number?
|
|
// The amount to skip before a micro tick is drawn. Default is 1.
|
|
// labels: Object[]?
|
|
// An array of labels for major ticks, with corresponding numeric values, ordered by value.
|
|
// labelFunc: Function?
|
|
// An optional function used to compute label values.
|
|
// maxLabelSize: Number?
|
|
// The maximum size, in pixels, for a label. To be used with the optional label function.
|
|
// stroke: dojox.gfx.Stroke?
|
|
// An optional stroke to be used for drawing an axis.
|
|
// majorTick: Object?
|
|
// An object containing a dojox.gfx.Stroke, and a length (number) for a major tick.
|
|
// minorTick: Object?
|
|
// An object containing a dojox.gfx.Stroke, and a length (number) for a minor tick.
|
|
// microTick: Object?
|
|
// An object containing a dojox.gfx.Stroke, and a length (number) for a micro tick.
|
|
// tick: Object?
|
|
// An object containing a dojox.gfx.Stroke, and a length (number) for a tick.
|
|
// font: String?
|
|
// An optional font definition (as used in the CSS font property) for labels.
|
|
// fontColor: String|dojo.Color?
|
|
// An optional color to be used in drawing labels.
|
|
// enableCache: Boolean?
|
|
// Whether the ticks and labels are cached from one rendering to another. This improves the rendering performance of
|
|
// successive rendering but penalize the first rendering. For labels it is only working with gfx labels
|
|
// not html ones. Default false.
|
|
|
|
this.vertical = vertical;
|
|
this.fixUpper = fixUpper;
|
|
this.fixLower = fixLower;
|
|
this.natural = natural;
|
|
this.leftBottom = leftBottom;
|
|
this.includeZero = includeZero;
|
|
this.fixed = fixed;
|
|
this.majorLabels = majorLabels;
|
|
this.minorTicks = minorTicks;
|
|
this.minorLabels = minorLabels;
|
|
this.microTicks = microTicks;
|
|
this.htmlLabels = htmlLabels;
|
|
this.min = min;
|
|
this.max = max;
|
|
this.from = from;
|
|
this.to = to;
|
|
this.majorTickStep = majorTickStep;
|
|
this.minorTickStep = minorTickStep;
|
|
this.microTickStep = microTickStep;
|
|
this.labels = labels;
|
|
this.labelFunc = labelFunc;
|
|
this.maxLabelSize = maxLabelSize;
|
|
this.stroke = stroke;
|
|
this.majorTick = majorTick;
|
|
this.minorTick = minorTick;
|
|
this.microTick = microTick;
|
|
this.tick = tick;
|
|
this.font = font;
|
|
this.fontColor = fontColor;
|
|
this.enableCache = enableCache;
|
|
}
|
|
var Invisible = dojox.charting.axis2d.Invisible
|
|
=====*/
|
|
|
|
var labelGap = 4, // in pixels
|
|
centerAnchorLimit = 45; // in degrees
|
|
|
|
return declare("dojox.charting.axis2d.Default", Invisible, {
|
|
// summary:
|
|
// The default axis object used in dojox.charting. See dojox.charting.Chart.addAxis for details.
|
|
//
|
|
// defaultParams: Object
|
|
// The default parameters used to define any axis.
|
|
// optionalParams: Object
|
|
// Any optional parameters needed to define an axis.
|
|
|
|
/*
|
|
// TODO: the documentation tools need these to be pre-defined in order to pick them up
|
|
// correctly, but the code here is partially predicated on whether or not the properties
|
|
// actually exist. For now, we will leave these undocumented but in the code for later. -- TRT
|
|
|
|
// opt: Object
|
|
// The actual options used to define this axis, created at initialization.
|
|
// scalar: Object
|
|
// The calculated helper object to tell charts how to draw an axis and any data.
|
|
// ticks: Object
|
|
// The calculated tick object that helps a chart draw the scaling on an axis.
|
|
// dirty: Boolean
|
|
// The state of the axis (whether it needs to be redrawn or not)
|
|
// scale: Number
|
|
// The current scale of the axis.
|
|
// offset: Number
|
|
// The current offset of the axis.
|
|
|
|
opt: null,
|
|
scalar: null,
|
|
ticks: null,
|
|
dirty: true,
|
|
scale: 1,
|
|
offset: 0,
|
|
*/
|
|
defaultParams: {
|
|
vertical: false, // true for vertical axis
|
|
fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none"
|
|
fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none"
|
|
natural: false, // all tick marks should be made on natural numbers
|
|
leftBottom: true, // position of the axis, used with "vertical"
|
|
includeZero: false, // 0 should be included
|
|
fixed: true, // all labels are fixed numbers
|
|
majorLabels: true, // draw major labels
|
|
minorTicks: true, // draw minor ticks
|
|
minorLabels: true, // draw minor labels
|
|
microTicks: false, // draw micro ticks
|
|
rotation: 0, // label rotation angle in degrees
|
|
htmlLabels: true, // use HTML to draw labels
|
|
enableCache: false // whether we cache or not
|
|
},
|
|
optionalParams: {
|
|
min: 0, // minimal value on this axis
|
|
max: 1, // maximal value on this axis
|
|
from: 0, // visible from this value
|
|
to: 1, // visible to this value
|
|
majorTickStep: 4, // major tick step
|
|
minorTickStep: 2, // minor tick step
|
|
microTickStep: 1, // micro tick step
|
|
labels: [], // array of labels for major ticks
|
|
// with corresponding numeric values
|
|
// ordered by values
|
|
labelFunc: null, // function to compute label values
|
|
maxLabelSize: 0, // size in px. For use with labelFunc
|
|
maxLabelCharCount: 0, // size in word count.
|
|
trailingSymbol: null,
|
|
|
|
// TODO: add support for minRange!
|
|
// minRange: 1, // smallest distance from min allowed on the axis
|
|
|
|
// theme components
|
|
stroke: {}, // stroke for an axis
|
|
majorTick: {}, // stroke + length for a tick
|
|
minorTick: {}, // stroke + length for a tick
|
|
microTick: {}, // stroke + length for a tick
|
|
tick: {}, // stroke + length for a tick
|
|
font: "", // font for labels
|
|
fontColor: "", // color for labels as a string
|
|
title: "", // axis title
|
|
titleGap: 0, // gap between axis title and axis label
|
|
titleFont: "", // axis title font
|
|
titleFontColor: "", // axis title font color
|
|
titleOrientation: "" // "axis" means the title facing the axis, "away" means facing away
|
|
},
|
|
|
|
constructor: function(chart, kwArgs){
|
|
// summary:
|
|
// The constructor for an axis.
|
|
// chart: dojox.charting.Chart
|
|
// The chart the axis belongs to.
|
|
// kwArgs: dojox.charting.axis2d.__AxisCtorArgs?
|
|
// Any optional keyword arguments to be used to define this axis.
|
|
this.opt = lang.clone(this.defaultParams);
|
|
du.updateWithObject(this.opt, kwArgs);
|
|
du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
|
|
if(this.opt.enableCache){
|
|
this._textFreePool = [];
|
|
this._lineFreePool = [];
|
|
this._textUsePool = [];
|
|
this._lineUsePool = [];
|
|
}
|
|
},
|
|
getOffsets: function(){
|
|
// summary:
|
|
// Get the physical offset values for this axis (used in drawing data series).
|
|
// returns: Object
|
|
// The calculated offsets in the form of { l, r, t, b } (left, right, top, bottom).
|
|
var s = this.scaler, offsets = { l: 0, r: 0, t: 0, b: 0 };
|
|
if(!s){
|
|
return offsets;
|
|
}
|
|
var o = this.opt, labelWidth = 0, a, b, c, d,
|
|
gl = scommon.getNumericLabel,
|
|
offset = 0, ma = s.major, mi = s.minor,
|
|
ta = this.chart.theme.axis,
|
|
// TODO: we use one font --- of major tick, we need to use major and minor fonts
|
|
taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font),
|
|
taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont),
|
|
taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15,
|
|
taMajorTick = this.chart.theme.getTick("major", o),
|
|
taMinorTick = this.chart.theme.getTick("minor", o),
|
|
size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0,
|
|
tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0,
|
|
rotation = o.rotation % 360, leftBottom = o.leftBottom,
|
|
cosr = Math.abs(Math.cos(rotation * Math.PI / 180)),
|
|
sinr = Math.abs(Math.sin(rotation * Math.PI / 180));
|
|
this.trailingSymbol = (o.trailingSymbol === undefined || o.trailingSymbol === null) ? this.trailingSymbol : o.trailingSymbol;
|
|
if(rotation < 0){
|
|
rotation += 360;
|
|
}
|
|
|
|
if(size){
|
|
// we need width of all labels
|
|
if(this.labels){
|
|
labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount);
|
|
}else{
|
|
labelWidth = this._groupLabelWidth([
|
|
gl(ma.start, ma.prec, o),
|
|
gl(ma.start + ma.count * ma.tick, ma.prec, o),
|
|
gl(mi.start, mi.prec, o),
|
|
gl(mi.start + mi.count * mi.tick, mi.prec, o)
|
|
], taFont, o.maxLabelCharCount);
|
|
}
|
|
labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth;
|
|
if(this.vertical){
|
|
var side = leftBottom ? "l" : "r";
|
|
switch(rotation){
|
|
case 0:
|
|
case 180:
|
|
offsets[side] = labelWidth;
|
|
offsets.t = offsets.b = size / 2;
|
|
break;
|
|
case 90:
|
|
case 270:
|
|
offsets[side] = size;
|
|
offsets.t = offsets.b = labelWidth / 2;
|
|
break;
|
|
default:
|
|
if(rotation <= centerAnchorLimit || (180 < rotation && rotation <= (180 + centerAnchorLimit))){
|
|
offsets[side] = size * sinr / 2 + labelWidth * cosr;
|
|
offsets[leftBottom ? "t" : "b"] = size * cosr / 2 + labelWidth * sinr;
|
|
offsets[leftBottom ? "b" : "t"] = size * cosr / 2;
|
|
}else if(rotation > (360 - centerAnchorLimit) || (180 > rotation && rotation > (180 - centerAnchorLimit))){
|
|
offsets[side] = size * sinr / 2 + labelWidth * cosr;
|
|
offsets[leftBottom ? "b" : "t"] = size * cosr / 2 + labelWidth * sinr;
|
|
offsets[leftBottom ? "t" : "b"] = size * cosr / 2;
|
|
}else if(rotation < 90 || (180 < rotation && rotation < 270)){
|
|
offsets[side] = size * sinr + labelWidth * cosr;
|
|
offsets[leftBottom ? "t" : "b"] = size * cosr + labelWidth * sinr;
|
|
}else{
|
|
offsets[side] = size * sinr + labelWidth * cosr;
|
|
offsets[leftBottom ? "b" : "t"] = size * cosr + labelWidth * sinr;
|
|
}
|
|
break;
|
|
}
|
|
offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0);
|
|
}else{
|
|
var side = leftBottom ? "b" : "t";
|
|
switch(rotation){
|
|
case 0:
|
|
case 180:
|
|
offsets[side] = size;
|
|
offsets.l = offsets.r = labelWidth / 2;
|
|
break;
|
|
case 90:
|
|
case 270:
|
|
offsets[side] = labelWidth;
|
|
offsets.l = offsets.r = size / 2;
|
|
break;
|
|
default:
|
|
if((90 - centerAnchorLimit) <= rotation && rotation <= 90 || (270 - centerAnchorLimit) <= rotation && rotation <= 270){
|
|
offsets[side] = size * sinr / 2 + labelWidth * cosr;
|
|
offsets[leftBottom ? "r" : "l"] = size * cosr / 2 + labelWidth * sinr;
|
|
offsets[leftBottom ? "l" : "r"] = size * cosr / 2;
|
|
}else if(90 <= rotation && rotation <= (90 + centerAnchorLimit) || 270 <= rotation && rotation <= (270 + centerAnchorLimit)){
|
|
offsets[side] = size * sinr / 2 + labelWidth * cosr;
|
|
offsets[leftBottom ? "l" : "r"] = size * cosr / 2 + labelWidth * sinr;
|
|
offsets[leftBottom ? "r" : "l"] = size * cosr / 2;
|
|
}else if(rotation < centerAnchorLimit || (180 < rotation && rotation < (180 - centerAnchorLimit))){
|
|
offsets[side] = size * sinr + labelWidth * cosr;
|
|
offsets[leftBottom ? "r" : "l"] = size * cosr + labelWidth * sinr;
|
|
}else{
|
|
offsets[side] = size * sinr + labelWidth * cosr;
|
|
offsets[leftBottom ? "l" : "r"] = size * cosr + labelWidth * sinr;
|
|
}
|
|
break;
|
|
}
|
|
offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0);
|
|
}
|
|
}
|
|
if(labelWidth){
|
|
this._cachedLabelWidth = labelWidth;
|
|
}
|
|
return offsets; // Object
|
|
},
|
|
cleanGroup: function(creator){
|
|
if(this.opt.enableCache && this.group){
|
|
this._lineFreePool = this._lineFreePool.concat(this._lineUsePool);
|
|
this._lineUsePool = [];
|
|
this._textFreePool = this._textFreePool.concat(this._textUsePool);
|
|
this._textUsePool = [];
|
|
}
|
|
this.inherited(arguments);
|
|
},
|
|
createText: function(labelType, creator, x, y, align, textContent, font, fontColor, labelWidth){
|
|
if(!this.opt.enableCache || labelType=="html"){
|
|
return acommon.createText[labelType](
|
|
this.chart,
|
|
creator,
|
|
x,
|
|
y,
|
|
align,
|
|
textContent,
|
|
font,
|
|
fontColor,
|
|
labelWidth
|
|
);
|
|
}
|
|
var text;
|
|
if (this._textFreePool.length > 0){
|
|
text = this._textFreePool.pop();
|
|
text.setShape({x: x, y: y, text: textContent, align: align});
|
|
// For now all items share the same font, no need to re-set it
|
|
//.setFont(font).setFill(fontColor);
|
|
// was cleared, add it back
|
|
creator.add(text);
|
|
}else{
|
|
text = acommon.createText[labelType](
|
|
this.chart,
|
|
creator,
|
|
x,
|
|
y,
|
|
align,
|
|
textContent,
|
|
font,
|
|
fontColor,
|
|
labelWidth
|
|
); }
|
|
this._textUsePool.push(text);
|
|
return text;
|
|
},
|
|
createLine: function(creator, params){
|
|
var line;
|
|
if(this.opt.enableCache && this._lineFreePool.length > 0){
|
|
line = this._lineFreePool.pop();
|
|
line.setShape(params);
|
|
// was cleared, add it back
|
|
creator.add(line);
|
|
}else{
|
|
line = creator.createLine(params);
|
|
}
|
|
if(this.opt.enableCache){
|
|
this._lineUsePool.push(line);
|
|
}
|
|
return line;
|
|
},
|
|
render: function(dim, offsets){
|
|
// summary:
|
|
// Render/draw the axis.
|
|
// dim: Object
|
|
// An object of the form { width, height}.
|
|
// offsets: Object
|
|
// An object of the form { l, r, t, b }.
|
|
// returns: dojox.charting.axis2d.Default
|
|
// The reference to the axis for functional chaining.
|
|
if(!this.dirty){
|
|
return this; // dojox.charting.axis2d.Default
|
|
}
|
|
// prepare variable
|
|
var o = this.opt, ta = this.chart.theme.axis, leftBottom = o.leftBottom, rotation = o.rotation % 360,
|
|
start, stop, titlePos, titleRotation=0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign,
|
|
|
|
// TODO: we use one font --- of major tick, we need to use major and minor fonts
|
|
taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font),
|
|
taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont),
|
|
// TODO: we use one font color --- we need to use different colors
|
|
taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black",
|
|
taTitleFontColor = o.titleFontColor || (ta.tick && ta.tick.titleFontColor) || "black",
|
|
taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15,
|
|
taTitleOrientation = o.titleOrientation || (ta.tick && ta.tick.titleOrientation) || "axis",
|
|
taMajorTick = this.chart.theme.getTick("major", o),
|
|
taMinorTick = this.chart.theme.getTick("minor", o),
|
|
taMicroTick = this.chart.theme.getTick("micro", o),
|
|
|
|
tickSize = Math.max(taMajorTick.length, taMinorTick.length, taMicroTick.length),
|
|
taStroke = "stroke" in o ? o.stroke : ta.stroke,
|
|
size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0,
|
|
cosr = Math.abs(Math.cos(rotation * Math.PI / 180)),
|
|
sinr = Math.abs(Math.sin(rotation * Math.PI / 180)),
|
|
tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0;
|
|
if(rotation < 0){
|
|
rotation += 360;
|
|
}
|
|
if(this.vertical){
|
|
start = {y: dim.height - offsets.b};
|
|
stop = {y: offsets.t};
|
|
titlePos = {y: (dim.height - offsets.b + offsets.t)/2};
|
|
titleOffset = size * sinr + (this._cachedLabelWidth || 0) * cosr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap;
|
|
axisVector = {x: 0, y: -1};
|
|
labelOffset = {x: 0, y: 0};
|
|
tickVector = {x: 1, y: 0};
|
|
anchorOffset = {x: labelGap, y: 0};
|
|
switch(rotation){
|
|
case 0:
|
|
labelAlign = "end";
|
|
labelOffset.y = size * 0.4;
|
|
break;
|
|
case 90:
|
|
labelAlign = "middle";
|
|
labelOffset.x = -size;
|
|
break;
|
|
case 180:
|
|
labelAlign = "start";
|
|
labelOffset.y = -size * 0.4;
|
|
break;
|
|
case 270:
|
|
labelAlign = "middle";
|
|
break;
|
|
default:
|
|
if(rotation < centerAnchorLimit){
|
|
labelAlign = "end";
|
|
labelOffset.y = size * 0.4;
|
|
}else if(rotation < 90){
|
|
labelAlign = "end";
|
|
labelOffset.y = size * 0.4;
|
|
}else if(rotation < (180 - centerAnchorLimit)){
|
|
labelAlign = "start";
|
|
}else if(rotation < (180 + centerAnchorLimit)){
|
|
labelAlign = "start";
|
|
labelOffset.y = -size * 0.4;
|
|
}else if(rotation < 270){
|
|
labelAlign = "start";
|
|
labelOffset.x = leftBottom ? 0 : size * 0.4;
|
|
}else if(rotation < (360 - centerAnchorLimit)){
|
|
labelAlign = "end";
|
|
labelOffset.x = leftBottom ? 0 : size * 0.4;
|
|
}else{
|
|
labelAlign = "end";
|
|
labelOffset.y = size * 0.4;
|
|
}
|
|
}
|
|
if(leftBottom){
|
|
start.x = stop.x = offsets.l;
|
|
titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 90 : 270;
|
|
titlePos.x = offsets.l - titleOffset + (titleRotation == 270 ? tsize : 0);
|
|
tickVector.x = -1;
|
|
anchorOffset.x = -anchorOffset.x;
|
|
}else{
|
|
start.x = stop.x = dim.width - offsets.r;
|
|
titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 90 : 270;
|
|
titlePos.x = dim.width - offsets.r + titleOffset - (titleRotation == 270 ? 0 : tsize);
|
|
switch(labelAlign){
|
|
case "start":
|
|
labelAlign = "end";
|
|
break;
|
|
case "end":
|
|
labelAlign = "start";
|
|
break;
|
|
case "middle":
|
|
labelOffset.x += size;
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
start = {x: offsets.l};
|
|
stop = {x: dim.width - offsets.r};
|
|
titlePos = {x: (dim.width - offsets.r + offsets.l)/2};
|
|
titleOffset = size * cosr + (this._cachedLabelWidth || 0) * sinr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap;
|
|
axisVector = {x: 1, y: 0};
|
|
labelOffset = {x: 0, y: 0};
|
|
tickVector = {x: 0, y: 1};
|
|
anchorOffset = {x: 0, y: labelGap};
|
|
switch(rotation){
|
|
case 0:
|
|
labelAlign = "middle";
|
|
labelOffset.y = size;
|
|
break;
|
|
case 90:
|
|
labelAlign = "start";
|
|
labelOffset.x = -size * 0.4;
|
|
break;
|
|
case 180:
|
|
labelAlign = "middle";
|
|
break;
|
|
case 270:
|
|
labelAlign = "end";
|
|
labelOffset.x = size * 0.4;
|
|
break;
|
|
default:
|
|
if(rotation < (90 - centerAnchorLimit)){
|
|
labelAlign = "start";
|
|
labelOffset.y = leftBottom ? size : 0;
|
|
}else if(rotation < (90 + centerAnchorLimit)){
|
|
labelAlign = "start";
|
|
labelOffset.x = -size * 0.4;
|
|
}else if(rotation < 180){
|
|
labelAlign = "start";
|
|
labelOffset.y = leftBottom ? 0 : -size;
|
|
}else if(rotation < (270 - centerAnchorLimit)){
|
|
labelAlign = "end";
|
|
labelOffset.y = leftBottom ? 0 : -size;
|
|
}else if(rotation < (270 + centerAnchorLimit)){
|
|
labelAlign = "end";
|
|
labelOffset.y = leftBottom ? size * 0.4 : 0;
|
|
}else{
|
|
labelAlign = "end";
|
|
labelOffset.y = leftBottom ? size : 0;
|
|
}
|
|
}
|
|
if(leftBottom){
|
|
start.y = stop.y = dim.height - offsets.b;
|
|
titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 180 : 0;
|
|
titlePos.y = dim.height - offsets.b + titleOffset - (titleRotation ? tsize : 0);
|
|
}else{
|
|
start.y = stop.y = offsets.t;
|
|
titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0;
|
|
titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize);
|
|
tickVector.y = -1;
|
|
anchorOffset.y = -anchorOffset.y;
|
|
switch(labelAlign){
|
|
case "start":
|
|
labelAlign = "end";
|
|
break;
|
|
case "end":
|
|
labelAlign = "start";
|
|
break;
|
|
case "middle":
|
|
labelOffset.y -= size;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// render shapes
|
|
|
|
this.cleanGroup();
|
|
|
|
try{
|
|
var s = this.group,
|
|
c = this.scaler,
|
|
t = this.ticks,
|
|
canLabel,
|
|
f = lin.getTransformerFromModel(this.scaler),
|
|
// GFX Canvas now supports labels, so let's _not_ fallback to HTML anymore on canvas, just use
|
|
// HTML labels if explicitly asked + no rotation + no IE + no Opera
|
|
labelType = (!o.title || !titleRotation) && !rotation && this.opt.htmlLabels && !has("ie") && !has("opera") ? "html" : "gfx",
|
|
dx = tickVector.x * taMajorTick.length,
|
|
dy = tickVector.y * taMajorTick.length;
|
|
|
|
s.createLine({
|
|
x1: start.x,
|
|
y1: start.y,
|
|
x2: stop.x,
|
|
y2: stop.y
|
|
}).setStroke(taStroke);
|
|
|
|
//create axis title
|
|
if(o.title){
|
|
var axisTitle = acommon.createText[labelType](
|
|
this.chart,
|
|
s,
|
|
titlePos.x,
|
|
titlePos.y,
|
|
"middle",
|
|
o.title,
|
|
taTitleFont,
|
|
taTitleFontColor
|
|
);
|
|
if(labelType == "html"){
|
|
this.htmlElements.push(axisTitle);
|
|
}else{
|
|
//as soon as rotation is provided, labelType won't be "html"
|
|
//rotate gfx labels
|
|
axisTitle.setTransform(g.matrix.rotategAt(titleRotation, titlePos.x, titlePos.y));
|
|
}
|
|
}
|
|
|
|
// go out nicely instead of try/catch
|
|
if(t==null){
|
|
this.dirty = false;
|
|
return this;
|
|
}
|
|
|
|
arr.forEach(t.major, function(tick){
|
|
var offset = f(tick.value), elem,
|
|
x = start.x + axisVector.x * offset,
|
|
y = start.y + axisVector.y * offset;
|
|
this.createLine(s, {
|
|
x1: x, y1: y,
|
|
x2: x + dx,
|
|
y2: y + dy
|
|
}).setStroke(taMajorTick);
|
|
if(tick.label){
|
|
var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : {
|
|
text: tick.label,
|
|
truncated: false
|
|
};
|
|
label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label;
|
|
elem = this.createText(labelType,
|
|
s,
|
|
x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x),
|
|
y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y),
|
|
labelAlign,
|
|
label.text,
|
|
taFont,
|
|
taFontColor
|
|
//this._cachedLabelWidth
|
|
);
|
|
|
|
// if bidi support was required, the textDir is "auto" and truncation
|
|
// took place, we need to update the dir of the element for cases as:
|
|
// Fool label: 111111W (W for bidi character)
|
|
// truncated label: 11...
|
|
// in this case for auto textDir the dir will be "ltr" which is wrong.
|
|
if(this.chart.truncateBidi && label.truncated){
|
|
this.chart.truncateBidi(elem, tick.label, labelType);
|
|
}
|
|
label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType);
|
|
if(labelType == "html"){
|
|
this.htmlElements.push(elem);
|
|
}else if(rotation){
|
|
elem.setTransform([
|
|
{dx: labelOffset.x, dy: labelOffset.y},
|
|
g.matrix.rotategAt(
|
|
rotation,
|
|
x + dx + anchorOffset.x,
|
|
y + dy + anchorOffset.y
|
|
)
|
|
]);
|
|
}
|
|
}
|
|
}, this);
|
|
|
|
dx = tickVector.x * taMinorTick.length;
|
|
dy = tickVector.y * taMinorTick.length;
|
|
canLabel = c.minMinorStep <= c.minor.tick * c.bounds.scale;
|
|
arr.forEach(t.minor, function(tick){
|
|
var offset = f(tick.value), elem,
|
|
x = start.x + axisVector.x * offset,
|
|
y = start.y + axisVector.y * offset;
|
|
this.createLine(s, {
|
|
x1: x, y1: y,
|
|
x2: x + dx,
|
|
y2: y + dy
|
|
}).setStroke(taMinorTick);
|
|
if(canLabel && tick.label){
|
|
var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : {
|
|
text: tick.label,
|
|
truncated: false
|
|
};
|
|
label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label;
|
|
elem = this.createText(labelType,
|
|
s,
|
|
x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x),
|
|
y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y),
|
|
labelAlign,
|
|
label.text,
|
|
taFont,
|
|
taFontColor
|
|
//this._cachedLabelWidth
|
|
);
|
|
// if bidi support was required, the textDir is "auto" and truncation
|
|
// took place, we need to update the dir of the element for cases as:
|
|
// Fool label: 111111W (W for bidi character)
|
|
// truncated label: 11...
|
|
// in this case for auto textDir the dir will be "ltr" which is wrong.
|
|
if(this.chart.getTextDir && label.truncated){
|
|
this.chart.truncateBidi(elem, tick.label, labelType);
|
|
}
|
|
label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType);
|
|
if(labelType == "html"){
|
|
this.htmlElements.push(elem);
|
|
}else if(rotation){
|
|
elem.setTransform([
|
|
{dx: labelOffset.x, dy: labelOffset.y},
|
|
g.matrix.rotategAt(
|
|
rotation,
|
|
x + dx + anchorOffset.x,
|
|
y + dy + anchorOffset.y
|
|
)
|
|
]);
|
|
}
|
|
}
|
|
}, this);
|
|
|
|
dx = tickVector.x * taMicroTick.length;
|
|
dy = tickVector.y * taMicroTick.length;
|
|
arr.forEach(t.micro, function(tick){
|
|
var offset = f(tick.value), elem,
|
|
x = start.x + axisVector.x * offset,
|
|
y = start.y + axisVector.y * offset;
|
|
this.createLine(s, {
|
|
x1: x, y1: y,
|
|
x2: x + dx,
|
|
y2: y + dy
|
|
}).setStroke(taMicroTick);
|
|
}, this);
|
|
}catch(e){
|
|
// squelch
|
|
}
|
|
|
|
this.dirty = false;
|
|
return this; // dojox.charting.axis2d.Default
|
|
},
|
|
labelTooltip: function(elem, chart, label, truncatedLabel, font, elemType){
|
|
var modules = ["dijit/Tooltip"];
|
|
var aroundRect = {type: "rect"}, position = ["above", "below"],
|
|
fontWidth = g._base._getTextBox(truncatedLabel, {font: font}).w || 0,
|
|
fontHeight = font ? g.normalizedLength(g.splitFontString(font).size) : 0;
|
|
if(elemType == "html"){
|
|
lang.mixin(aroundRect, html.coords(elem.firstChild, true));
|
|
aroundRect.width = Math.ceil(fontWidth);
|
|
aroundRect.height = Math.ceil(fontHeight);
|
|
this._events.push({
|
|
shape: dojo,
|
|
handle: connect.connect(elem.firstChild, "onmouseover", this, function(e){
|
|
require(modules, function(Tooltip){
|
|
Tooltip.show(label, aroundRect, position);
|
|
});
|
|
})
|
|
});
|
|
this._events.push({
|
|
shape: dojo,
|
|
handle: connect.connect(elem.firstChild, "onmouseout", this, function(e){
|
|
require(modules, function(Tooltip){
|
|
Tooltip.hide(aroundRect);
|
|
});
|
|
})
|
|
});
|
|
}else{
|
|
var shp = elem.getShape(),
|
|
lt = html.coords(chart.node, true);
|
|
aroundRect = lang.mixin(aroundRect, {
|
|
x: shp.x - fontWidth / 2,
|
|
y: shp.y
|
|
});
|
|
aroundRect.x += lt.x;
|
|
aroundRect.y += lt.y;
|
|
aroundRect.x = Math.round(aroundRect.x);
|
|
aroundRect.y = Math.round(aroundRect.y);
|
|
aroundRect.width = Math.ceil(fontWidth);
|
|
aroundRect.height = Math.ceil(fontHeight);
|
|
this._events.push({
|
|
shape: elem,
|
|
handle: elem.connect("onmouseenter", this, function(e){
|
|
require(modules, function(Tooltip){
|
|
Tooltip.show(label, aroundRect, position);
|
|
});
|
|
})
|
|
});
|
|
this._events.push({
|
|
shape: elem,
|
|
handle: elem.connect("onmouseleave", this, function(e){
|
|
require(modules, function(Tooltip){
|
|
Tooltip.hide(aroundRect);
|
|
});
|
|
})
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|