diff options
Diffstat (limited to 'js/dojo/dojox/charting/axis2d')
| -rw-r--r-- | js/dojo/dojox/charting/axis2d/Base.js | 72 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/axis2d/Default.js | 800 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/axis2d/Invisible.js | 286 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/axis2d/common.js | 162 |
4 files changed, 1320 insertions, 0 deletions
diff --git a/js/dojo/dojox/charting/axis2d/Base.js b/js/dojo/dojox/charting/axis2d/Base.js new file mode 100644 index 0000000..5c0f690 --- /dev/null +++ b/js/dojo/dojox/charting/axis2d/Base.js @@ -0,0 +1,72 @@ +//>>built +define("dojox/charting/axis2d/Base", ["dojo/_base/declare", "../Element"], + function(declare, Element){ +/*===== +var Element = dojox.charting.Element; +=====*/ +return declare("dojox.charting.axis2d.Base", Element, { + // summary: + // The base class for any axis. This is more of an interface/API + // definition than anything else; see dojox.charting.axis2d.Default + // for more details. + constructor: function(chart, kwArgs){ + // summary: + // Return a new base axis. + // chart: dojox.charting.Chart + // The chart this axis belongs to. + // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? + // An optional arguments object to define the axis parameters. + this.vertical = kwArgs && kwArgs.vertical; + }, + clear: function(){ + // summary: + // Stub function for clearing the axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + return this; // dojox.charting.axis2d.Base + }, + initialized: function(){ + // summary: + // Return a flag as to whether or not this axis has been initialized. + // returns: Boolean + // If the axis is initialized or not. + return false; // Boolean + }, + calculate: function(min, max, span){ + // summary: + // Stub function to run the calcuations needed for drawing this axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + return this; // dojox.charting.axis2d.Base + }, + getScaler: function(){ + // summary: + // A stub function to return the scaler object created during calculate. + // returns: Object + // The scaler object (see dojox.charting.scaler.linear for more information) + return null; // Object + }, + getTicks: function(){ + // summary: + // A stub function to return the object that helps define how ticks are rendered. + // returns: Object + // The ticks object. + return null; // Object + }, + getOffsets: function(){ + // summary: + // A stub function to return any offsets needed for axis and series rendering. + // returns: Object + // An object of the form { l, r, t, b }. + return {l: 0, r: 0, t: 0, b: 0}; // Object + }, + render: function(dim, offsets){ + // summary: + // Stub function to render this axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + this.dirty = false; + return this; // dojox.charting.axis2d.Base + } +}); +}); diff --git a/js/dojo/dojox/charting/axis2d/Default.js b/js/dojo/dojox/charting/axis2d/Default.js new file mode 100644 index 0000000..9428e9d --- /dev/null +++ b/js/dojo/dojox/charting/axis2d/Default.js @@ -0,0 +1,800 @@ +//>>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); + }); + }) + }); + } + } + }); +}); diff --git a/js/dojo/dojox/charting/axis2d/Invisible.js b/js/dojo/dojox/charting/axis2d/Invisible.js new file mode 100644 index 0000000..81dd739 --- /dev/null +++ b/js/dojo/dojox/charting/axis2d/Invisible.js @@ -0,0 +1,286 @@ +//>>built +define("dojox/charting/axis2d/Invisible", ["dojo/_base/lang", "dojo/_base/declare", "./Base", "../scaler/linear", + "dojox/gfx", "dojox/lang/utils", "dojox/lang/functional", "dojo/string"], + function(lang, declare, Base, lin, g, du, df, dstring){ +/*===== +var Base = dojox.charting.axis2d.Base; +=====*/ + var merge = du.merge, + labelGap = 4, // in pixels + centerAnchorLimit = 45; // in degrees + + return declare("dojox.charting.axis2d.Invisible", Base, { + // 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 + }, + 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 + }, + + 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); + }, + dependOnData: function(){ + // summary: + // Find out whether or not the axis options depend on the data in the axis. + return !("min" in this.opt) || !("max" in this.opt); // Boolean + }, + clear: function(){ + // summary: + // Clear out all calculated properties on this axis; + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + delete this.scaler; + delete this.ticks; + this.dirty = true; + return this; // dojox.charting.axis2d.Default + }, + initialized: function(){ + // summary: + // Finds out if this axis has been initialized or not. + // returns: Boolean + // Whether a scaler has been calculated and if the axis is not dirty. + return "scaler" in this && !(this.dirty && this.dependOnData()); + }, + setWindow: function(scale, offset){ + // summary: + // Set the drawing "window" for the axis. + // scale: Number + // The new scale for the axis. + // offset: Number + // The new offset for the axis. + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + this.scale = scale; + this.offset = offset; + return this.clear(); // dojox.charting.axis2d.Default + }, + getWindowScale: function(){ + // summary: + // Get the current windowing scale of the axis. + return "scale" in this ? this.scale : 1; // Number + }, + getWindowOffset: function(){ + // summary: + // Get the current windowing offset for the axis. + return "offset" in this ? this.offset : 0; // Number + }, + _groupLabelWidth: function(labels, font, wcLimit){ + if(!labels.length){ + return 0; + } + if(lang.isObject(labels[0])){ + labels = df.map(labels, function(label){ return label.text; }); + } + if (wcLimit) { + labels = df.map(labels, function(label){ + return lang.trim(label).length == 0 ? "" : label.substring(0, wcLimit) + this.trailingSymbol; + }, this); + } + var s = labels.join("<br>"); + return g._base._getTextBox(s, {font: font}).w || 0; + }, + calculate: function(min, max, span, labels){ + // summary: + // Perform all calculations needed to render this axis. + // min: Number + // The smallest value represented on this axis. + // max: Number + // The largest value represented on this axis. + // span: Number + // The span in pixels over which axis calculations are made. + // labels: String[] + // Optional list of labels. + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + if(this.initialized()){ + return this; + } + var o = this.opt; + this.labels = "labels" in o ? o.labels : labels; + this.scaler = lin.buildScaler(min, max, span, o); + var tsb = this.scaler.bounds; + if("scale" in this){ + // calculate new range + o.from = tsb.lower + this.offset; + o.to = (tsb.upper - tsb.lower) / this.scale + o.from; + // make sure that bounds are correct + if( !isFinite(o.from) || + isNaN(o.from) || + !isFinite(o.to) || + isNaN(o.to) || + o.to - o.from >= tsb.upper - tsb.lower + ){ + // any error --- remove from/to bounds + delete o.from; + delete o.to; + delete this.scale; + delete this.offset; + }else{ + // shift the window, if we are out of bounds + if(o.from < tsb.lower){ + o.to += tsb.lower - o.from; + o.from = tsb.lower; + }else if(o.to > tsb.upper){ + o.from += tsb.upper - o.to; + o.to = tsb.upper; + } + // update the offset + this.offset = o.from - tsb.lower; + } + // re-calculate the scaler + this.scaler = lin.buildScaler(min, max, span, o); + tsb = this.scaler.bounds; + // cleanup + if(this.scale == 1 && this.offset == 0){ + delete this.scale; + delete this.offset; + } + } + + var ta = this.chart.theme.axis, labelWidth = 0, rotation = o.rotation % 360, + // 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), + 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)); + + if(rotation < 0){ + rotation += 360; + } + + if(size){ + if(this.vertical ? rotation != 0 && rotation != 180 : rotation != 90 && rotation != 270){ + // we need width of all labels + if(this.labels){ + labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount); + }else{ + var labelLength = Math.ceil( + Math.log( + Math.max( + Math.abs(tsb.from), + Math.abs(tsb.to) + ) + ) / Math.LN10 + ), + t = []; + if(tsb.from < 0 || tsb.to < 0){ + t.push("-"); + } + t.push(dstring.rep("9", labelLength)); + var precision = Math.floor( + Math.log( tsb.to - tsb.from ) / Math.LN10 + ); + if(precision > 0){ + t.push("."); + t.push(dstring.rep("9", precision)); + } + labelWidth = g._base._getTextBox( + t.join(""), + { font: taFont } + ).w; + } + labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth; + }else{ + labelWidth = size; + } + switch(rotation){ + case 0: + case 90: + case 180: + case 270: + // trivial cases: use labelWidth + break; + default: + // rotated labels + var gap1 = Math.sqrt(labelWidth * labelWidth + size * size), // short labels + gap2 = this.vertical ? size * cosr + labelWidth * sinr : labelWidth * cosr + size * sinr; // slanted labels + labelWidth = Math.min(gap1, gap2); + break; + } + } + + this.scaler.minMinorStep = labelWidth + labelGap; + this.ticks = lin.buildTicks(this.scaler, o); + return this; // dojox.charting.axis2d.Default + }, + getScaler: function(){ + // summary: + // Get the pre-calculated scaler object. + return this.scaler; // Object + }, + getTicks: function(){ + // summary: + // Get the pre-calculated ticks object. + return this.ticks; // Object + } + }); +}); diff --git a/js/dojo/dojox/charting/axis2d/common.js b/js/dojo/dojox/charting/axis2d/common.js new file mode 100644 index 0000000..2bb7e15 --- /dev/null +++ b/js/dojo/dojox/charting/axis2d/common.js @@ -0,0 +1,162 @@ +//>>built +define("dojox/charting/axis2d/common", ["dojo/_base/lang", "dojo/_base/html", "dojo/_base/window", "dojo/dom-geometry", "dojox/gfx"], + function(lang, html, win, domGeom, g){ + + var common = lang.getObject("dojox.charting.axis2d.common", true); + + var clearNode = function(s){ + s.marginLeft = "0px"; + s.marginTop = "0px"; + s.marginRight = "0px"; + s.marginBottom = "0px"; + s.paddingLeft = "0px"; + s.paddingTop = "0px"; + s.paddingRight = "0px"; + s.paddingBottom = "0px"; + s.borderLeftWidth = "0px"; + s.borderTopWidth = "0px"; + s.borderRightWidth = "0px"; + s.borderBottomWidth = "0px"; + }; + + var getBoxWidth = function(n){ + // marginBox is incredibly slow, so avoid it if we can + if(n["getBoundingClientRect"]){ + var bcr = n.getBoundingClientRect(); + return bcr.width || (bcr.right - bcr.left); + }else{ + return domGeom.getMarginBox(n).w; + } + }; + + return lang.mixin(common, { + // summary: + // Common methods to be used by any axis. This is considered "static". + createText: { + gfx: function(chart, creator, x, y, align, text, font, fontColor){ + // summary: + // Use dojox.gfx to create any text. + // chart: dojox.charting.Chart + // The chart to create the text into. + // creator: dojox.gfx.Surface + // The graphics surface to use for creating the text. + // x: Number + // Where to create the text along the x axis (CSS left). + // y: Number + // Where to create the text along the y axis (CSS top). + // align: String + // How to align the text. Can be "left", "right", "center". + // text: String + // The text to render. + // font: String + // The font definition, a la CSS "font". + // fontColor: String|dojo.Color + // The color of the resultant text. + // returns: dojox.gfx.Text + // The resultant GFX object. + return creator.createText({ + x: x, y: y, text: text, align: align + }).setFont(font).setFill(fontColor); // dojox.gfx.Text + }, + html: function(chart, creator, x, y, align, text, font, fontColor, labelWidth){ + // summary: + // Use the HTML DOM to create any text. + // chart: dojox.charting.Chart + // The chart to create the text into. + // creator: dojox.gfx.Surface + // The graphics surface to use for creating the text. + // x: Number + // Where to create the text along the x axis (CSS left). + // y: Number + // Where to create the text along the y axis (CSS top). + // align: String + // How to align the text. Can be "left", "right", "center". + // text: String + // The text to render. + // font: String + // The font definition, a la CSS "font". + // fontColor: String|dojo.Color + // The color of the resultant text. + // labelWidth: Number? + // The maximum width of the resultant DOM node. + // returns: DOMNode + // The resultant DOMNode (a "div" element). + + // setup the text node + var p = win.doc.createElement("div"), s = p.style, boxWidth; + // bidi support, if this function exists the module was loaded + if(chart.getTextDir){ + p.dir = chart.getTextDir(text); + } + clearNode(s); + s.font = font; + p.innerHTML = String(text).replace(/\s/g, " "); + s.color = fontColor; + // measure the size + s.position = "absolute"; + s.left = "-10000px"; + win.body().appendChild(p); + var size = g.normalizedLength(g.splitFontString(font).size); + + // do we need to calculate the label width? + if(!labelWidth){ + boxWidth = getBoxWidth(p); + } + // when the textDir is rtl, but the UI ltr needs + // to recalculate the starting point + if(p.dir == "rtl"){ + x += labelWidth ? labelWidth : boxWidth; + } + + // new settings for the text node + win.body().removeChild(p); + + s.position = "relative"; + if(labelWidth){ + s.width = labelWidth + "px"; + // s.border = "1px dotted grey"; + switch(align){ + case "middle": + s.textAlign = "center"; + s.left = (x - labelWidth / 2) + "px"; + break; + case "end": + s.textAlign = "right"; + s.left = (x - labelWidth) + "px"; + break; + default: + s.left = x + "px"; + s.textAlign = "left"; + break; + } + }else{ + switch(align){ + case "middle": + s.left = Math.floor(x - boxWidth / 2) + "px"; + // s.left = Math.floor(x - p.offsetWidth / 2) + "px"; + break; + case "end": + s.left = Math.floor(x - boxWidth) + "px"; + // s.left = Math.floor(x - p.offsetWidth) + "px"; + break; + //case "start": + default: + s.left = Math.floor(x) + "px"; + break; + } + } + s.top = Math.floor(y - size) + "px"; + s.whiteSpace = "nowrap"; // hack for WebKit + // setup the wrapper node + var wrap = win.doc.createElement("div"), w = wrap.style; + clearNode(w); + w.width = "0px"; + w.height = "0px"; + // insert nodes + wrap.appendChild(p) + chart.node.insertBefore(wrap, chart.node.firstChild); + return wrap; // DOMNode + } + } + }); +}); |
