META-INF.adf.jsLibsDebug.ApacheChart.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trinidad-impl Show documentation
Show all versions of trinidad-impl Show documentation
Private implementation of the Apache MyFaces Trinidad project
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/////////////////////////////////////////////
// Some utility functions
/////////////////////////////////////////////
/**
* Base Class that provides inheritence for charting subsystem
* @constructor
*/
function ApacheChartObj()
{
this.Init();
}
ApacheChartObj.prototype = new Object();
ApacheChartObj.prototype.constructor = ApacheChartObj;
ApacheChartObj._tempConstructor = function(){}
ApacheChartObj.Inherit = function(baseClass, extendingClass)
{
var tempConstructor = ApacheChartObj._tempConstructor;
tempConstructor.prototype = baseClass.prototype;
extendingClass.prototype = new tempConstructor();
extendingClass.prototype.constructor = extendingClass;
extendingClass.superclass = baseClass.prototype;
}
/**
* Asserts arg is true; else throws error with msg.
*/
ApacheChartObj.Assert = function(arg, msg)
{
if (!arg)
{
throw new Error(msg);
}
}
/**
* StringBuffer utility used by the charting
*/
function ApacheChartBuffer(size)
{
this.maxStreamLength = document.all?5000:100000;
this.data = new Array(size?size:100);
this.iStr = 0;
}
ApacheChartBuffer.prototype.append = function(obj)
{
this.data[this.iStr++] = obj;
if (this.data.length > this.maxStreamLength)
{
this.data = [this.data.join("")];
this.data.length = 100;
this.iStr = 1;
}
return this;
}
ApacheChartBuffer.prototype.toString = function()
{
return this.data.join("");
}
////////////////////////////////////////////////////////////////////
// Abstract Data Structure representing the model for drawing a chart control
////////////////////////////////////////////////////////////////////
function ApacheChartModel(seriesLabels, groupLabels, yValues, xValues, seriesColors)
{
// An array representing series labels
this._seriesLabels = seriesLabels;
// An array representing Group labels
this._groupLabels = groupLabels;
// A 2D array representing values for Y axis
this._yValues = yValues;
// A 2D array representing values for X axis.
// The x axis values are used only for scatter plots/XYline
this._xValues = xValues;
// The array of strings with colors. Used for display of the series
this._seriesColors = seriesColors;
var labelCount = seriesLabels.length;
var colorCount = seriesColors.length;
if(colorCount < labelCount)
{
var toHexFunc = ApacheChart._to_hex;
for(i = colorCount; i < labelCount; i++)
{
// generate random colors
var rVal = Math.floor(Math.random()*1000)%255;
var gVal = Math.floor(Math.random()*1000)%255;
var bVal = Math.floor(Math.random()*1000)%255;
seriesColors[i] = "#"+toHexFunc(rVal)+toHexFunc(gVal)+toHexFunc(bVal);
}
}
// the maximum value used to display the y-axis.
// Default is 120% of maximum of the yValues
//this._maxYValue = undefined;
// the minimum value used to display the y-axis. Default is 80% of minimum of values
//this._minYValue = undefined;
// the maximum value used to display the X-axis.
// Default is 120% of maximum of the xValues
//this._maxXValue = undefined;
// the minimum value used to display the y-axis. Default is 80% of minimum of values
//this._minXValue = undefined;
// The title for the graph
//this._title = undefined;
// The sub-title for the graph
//this._subTitle = undefined;
// The foot node for the graph
//this._footNote = undefined;
}
ApacheChartModel.prototype.getSeriesLabels = function()
{
return this._seriesLabels;
}
ApacheChartModel.prototype.getGroupLabels = function()
{
return this._groupLabels;
}
ApacheChartModel.prototype.getSeriesColors = function()
{
return this._seriesColors;
}
ApacheChartModel.prototype.getXValues = function()
{
return this._xValues;
}
ApacheChartModel.prototype.getYValues = function()
{
return this._yValues;
}
ApacheChartModel.prototype.setMaxYValue = function(maxYValue)
{
this._maxYValue = maxYValue;
}
ApacheChartModel.prototype.getMaxYValue = function()
{
return this._maxYValue;
}
ApacheChartModel.prototype.setMinYValue = function(minYValue)
{
this._minYValue = minYValue;
}
ApacheChartModel.prototype.getMinYValue = function()
{
return this._minYValue;
}
ApacheChartModel.prototype.setMaxXValue = function(maxXValue)
{
this._maxXValue = maxXValue;
}
ApacheChartModel.prototype.getMaxXValue = function()
{
return this._maxXValue;
}
ApacheChartModel.prototype.setMinXValue = function(minXValue)
{
this._minXValue = minXValue;
}
ApacheChartModel.prototype.getMinXValue = function()
{
return this._minXValue;
}
ApacheChartModel.prototype.setTitle = function(title)
{
this._title = title;
}
ApacheChartModel.prototype.getTitle = function()
{
return this._title;
}
ApacheChartModel.prototype.setSubTitle = function(subTitle)
{
this._subTitle = subTitle;
}
ApacheChartModel.prototype.getSubTitle = function()
{
return this._subTitle;
}
ApacheChartModel.prototype.setFootNote = function(footNote)
{
this._footNote = footNote;
}
ApacheChartModel.prototype.getFootNote = function()
{
return this._footNote;
}
////////////////////////////////////////////////////////////////////
// A Chart Event that is triggered in response to a user click
// This event can be marshalled to the server if integration is done
// with a J2EE platform platform
////////////////////////////////////////////////////////////////////
function ApacheChartEvent(seriesIndices, yValueIndices, yValues, xValues)
{
this._seriesIndices = seriesIndices;
this._yValueIndices = yValueIndices;
this._yValues = yValues;
this._xValues = xValues;
}
ApacheChartEvent.prototype.getSeriesIndices = function()
{
return this._seriesIndices;
}
ApacheChartEvent.prototype.getYValueIndices = function()
{
return this._yValueIndices;
}
ApacheChartEvent.prototype.getYValues = function()
{
return this._yValues;
}
ApacheChartEvent.prototype.getXValues = function()
{
return this._xValues;
}
ApacheChartEvent.prototype.toString = function()
{
var sb = new ApacheChartBuffer();
if(this._seriesIndices)
sb.append("seriesIndices = "+ this._seriesIndices.join(","));
if(this._yValueIndices)
sb.append("\yValueIndices = "+ this._yValueIndices.join(","));
sb.append("\nyValues = "+ this._yValues.join(","));
if(this._xValues)
sb.append("\nxValues = "+ this._xValues.join(","));
return sb.toString();
}
ApacheChartEvent.prototype.marshall = function()
{
var value = new Array();
if(this._seriesIndices)
value.push("seriesIndices\t"+this._seriesIndices.join("\t"));
if(this._yValueIndices)
value.push("yValueIndices\t"+this._yValueIndices.join("\t"));
value.push("yValues\t"+this._yValues.join("\t"));
if(this._xValues)
value.push("xValues\t"+this._xValues.join("\t"));
return value.join("$adf$");
}
////////////////////////////////////////////////////////////////////
// Abstract Base Class for rendering a Chart control
////////////////////////////////////////////////////////////////////
function ApacheChart(type, model, svgEmbedId, isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
// Hack for IE. The SVG has to be loaded externally otherwise,
// it needs to be activated by clicking
ApacheChart.createSVG = function(containerId,
svgEmbedId, sourceUrl, inlineStyle, styleClass)
{
var svgContainer = document.getElementById(containerId);
var embed = document.createElement("embed");
var agent = window._agent;
if(agent && agent.isIE)
{
var semiColIndex = sourceUrl.indexOf(";");
if(semiColIndex!=-1)
sourceUrl = sourceUrl.substr(0,semiColIndex);
}
embed.setAttribute("src",sourceUrl);
embed.setAttribute("id",svgEmbedId);
embed.setAttribute("wmode","transparent");
embed.setAttribute("type","image/svg+xml");
if(agent && agent.isOpera)
{
// opera does not like 100% width and 100% height.
var style = document.defaultView.getComputedStyle(svgContainer, null);
var embedStyle = embed.style;
embedStyle.width = style.width;
embedStyle.height = style.height;
}
else
{
embed.style.cssText = inlineStyle;
}
if(styleClass)
{
embed.className = styleClass;
}
svgContainer.appendChild(embed);
}
/**
* Method to detect if Adobe ActiveX controll is initialized on the machine
*/
ApacheChart.isASVInstalled = function()
{
try{
var asv = new ActiveXObject("Adobe.SVGCtl");
return true;
}
catch(e){
}
return false;
}
ApacheChartObj.Inherit(ApacheChartObj, ApacheChart);
ApacheChart.prototype.Init = function(type, model, svgEmbedId,
isPerspective, legendPosition)
{
this._type = type;
this._model = model;
this._svgEmbedId = svgEmbedId;
this._margins = {left:2,right:2,top:2,bottom:2};
// By default the graphs are drawn with a perspective(2.5D)
this._isPerspective = isPerspective;
this._legendPosition = legendPosition;
//this._rootElement = undefined;
this._toolTip = null;
this._toolTipVisible = false;
//this._svgDoc = undefined;
//this._width = undefined;
//this._height = undefined;
//this._vLabelContainer = undefined;
//this._hLabelContainer = undefined;
// By default the graph animates for 1.5 s
this._animDuration = 1500;
this._dataElems = [];
this._labelElems = [];
this._gridElems = [];
// The group labels
this._groupLabelElems = [];
// The number of Major Line sections to draw on y axis
this._yMajorGridCount = 8;
// The number of Minor Line section to draw on y axis
this._yMinorGridCount = -1;
// The number of Major Line sections to draw on x axis
this._xMajorGridCount = -1;
// Controls if the tooltips are displayed or not
this._tooltipsVisible = true;
// By default we will use gradients
this._gradientsUsed = true;
// The maximum precision of the numbers displayed on yaxis/tooltips
this._maxPrecision = 0;
// The decimal separator
this._decimalSep = null;
// The form from which we will submit
this._formName = null;
this._partialSubmit = true;
this._svgCheckTotal = 0;
this._errorTextNode = null;
this._isIE = false;
if(window._agent) // use trinidad agent
this._isIE = _agent.isIE;
if(this._isIE)
this._errorHtml = "Unable to load SVG plugin. Please install the plugin from Adobe";
else
this._errorHtml = "This component needs an SVG enabled browser like Internet Explorer, Firefox 1.5+ or Opera 9.0+";
this._statusHtml = "Please Wait. Attempting to load SVG document...
";
this.ComputeMinMaxValues();
}
/**
* Error text to be displayed in the container of the embed,
* in case the svg document fails to load.
* NOTE: the error text can be customized by browser for e.g. in IE it can contain
* the link to SVG plugin download
*/
ApacheChart.prototype.setErrorHtml = function(errorHtml)
{
this._errorHtml = errorHtml;
}
/**
* Status text that is displayed when the SVG document is taking a while to load
*/
ApacheChart.prototype.setStatusHtml = function(statusHtml)
{
this._statusHtml = statusHtml;
}
ApacheChart.prototype.setYMajorGridLineCount = function(count)
{
// number of sections is 1 greater than the lines
this._yMajorGridCount = count>0?count+1:count;
}
ApacheChart.prototype.setYMinorGridLineCount = function(count)
{
// number of sections is 1 greater than the lines
this._yMinorGridCount = count>0?count+1:count;
}
ApacheChart.prototype.setXMajorGridLineCount = function(count)
{
// number of sections is 1 greater than the lines
this._xMajorGridCount = count>0?count+1:count;
}
ApacheChart.prototype.setAnimationDuration = function(dur)
{
this._animDuration = dur;
}
ApacheChart.prototype.setGradientsUsed = function(gradient)
{
this._gradientsUsed = gradient;
}
ApacheChart.prototype.setMaxPrecision = function(precision)
{
this._maxPrecision = precision;
}
ApacheChart.prototype.setFormName = function(formName)
{
this._formName = formName;
}
ApacheChart.prototype.setPartialSubmit = function(partial)
{
this._partialSubmit = partial;
}
ApacheChart.prototype.setTooltipsVisible = function(visible)
{
this._tooltipsVisible = visible;
}
ApacheChart.prototype.getToolTip = function()
{
return this._toolTip;
}
ApacheChart.prototype.setToolTip = function(tt)
{
this._toolTip = tt;
}
ApacheChart.prototype.ComputeMinMaxValues = function()
{
var model = this._model, yValues = model.getYValues(), xValues = model.getXValues(),
maxYValue = model.getMaxYValue(), maxXValue = model.getMaxXValue(),
minYValue = model.getMinYValue(), minXValue = model.getMinXValue(),
seriesLabels = model.getSeriesLabels();
if(yValues != null && (maxYValue == null || minYValue == null))
{
var minMax = this._computeAxisMinMaxValues(yValues, seriesLabels.length);
if(maxYValue == null)
model.setMaxYValue(minMax.max);
if(minYValue == null)
model.setMinYValue(minMax.min);
}
if(xValues != null && (maxXValue == null || minXValue == null))
{
var minMax = this._computeAxisMinMaxValues(xValues, seriesLabels.length);
if(maxXValue == null)
model.setMaxXValue(minMax.max);
if(minXValue == null)
model.setMinXValue(minMax.min);
}
}
ApacheChart.prototype._computeAxisMinMaxValues = function(values, seriesSize)
{
var stackedTotal, value, maxValue = Number.NEGATIVE_INFINITY, minValue = Number.POSITIVE_INFINITY,
type = this._type, isStacked = false, groupsCount = values.length;
if(type == ApacheChart.TYPE_VBAR_STACKED || type == ApacheChart.TYPE_HBAR_STACKED ||
type == ApacheChart.TYPE_AREA_STACKED)
{
isStacked = true;
}
for (var i = 0; i < groupsCount; ++i)
{
stackedTotal = 0;
for (var j = 0; j < seriesSize; ++j)
{
value = values[i][j];
if (isStacked)
stackedTotal += value;
else
{
maxValue = Math.max(maxValue, value);
minValue = Math.min(minValue, value);
}
}
if (isStacked)
{
maxValue = Math.max(maxValue, stackedTotal);
minValue = Math.min(minValue, stackedTotal);
}
}
var maxMult = maxValue>0?ApacheChart._MAX_MULTIPLIER:ApacheChart._MIN_MULTIPLIER,
minMult = minValue>0?ApacheChart._MIN_MULTIPLIER:ApacheChart._MAX_MULTIPLIER;
return {max: maxValue*maxMult, min: minValue*minMult};
}
ApacheChart.TYPE_VBAR = 1;
ApacheChart.TYPE_HBAR = 2;
ApacheChart.TYPE_VBAR_STACKED = 3;
ApacheChart.TYPE_HBAR_STACKED = 4;
ApacheChart.TYPE_PIE = 5;
ApacheChart.TYPE_AREA = 6;
ApacheChart.TYPE_AREA_STACKED = 7;
ApacheChart.TYPE_LINE = 8;
ApacheChart.TYPE_BAR_LINE_COMBO = 9;
ApacheChart.TYPE_XYLINE = 10;
ApacheChart.TYPE_SCATTER_PLOT = 11;
ApacheChart.TYPE_RADAR = 12;
ApacheChart.TYPE_RADAR_AREA = 13;
ApacheChart.TYPE_FUNNEL = 14;
ApacheChart.CIRCULAR_GAUGE = 15;
ApacheChart.SEMI_CIRCULAR_GAUGE = 16;
ApacheChart.LEGEND_LOCATION_NONE = "none";
ApacheChart.LEGEND_LOCATION_TOP = "top";
ApacheChart.LEGEND_LOCATION_END = "end";
ApacheChart.LEGEND_LOCATION_BOTTOM = "bottom";
ApacheChart.LEGEND_LOCATION_START = "start";
ApacheChart._MAX_MULTIPLIER = 1.2;
ApacheChart._MIN_MULTIPLIER = .8;
ApacheChart._XOFFSET_PERSPECTIVE = 10;
ApacheChart._YOFFSET_PERSPECTIVE = 5;
// margin generally used around text
ApacheChart._TEXT_MARGIN = 4;
ApacheChart._DEFAULT_STOP_OPACITY = .9;
ApacheChart._BORDER_SIZE = 6;
// Animate at 15 fps
ApacheChart._ANIMATE_INTERVAL = 66;
ApacheChart._SVGCHECK_INTERVAL = 100;
ApacheChart._SVGCHECK_STATUS_LIMIT = 5000;
ApacheChart._SVGCHECK_MAX_LIMIT = 20000;
ApacheChart.createChart = function(
type,
model,
svgEmbedId,
isPerspective,
legendPosition)
{
var chart = null;
if(type == this.TYPE_VBAR || type == this.TYPE_VBAR_STACKED || type == this.TYPE_BAR_LINE_COMBO)
{
chart = new ApacheBarChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_HBAR || type == this.TYPE_HBAR_STACKED)
{
chart = new ApacheHBarChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_PIE)
{
chart = new ApachePieChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_AREA || type == this.TYPE_AREA_STACKED)
{
chart = new ApacheAreaChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_LINE)
{
chart = new ApacheLineChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_SCATTER_PLOT)
{
chart = new ApacheScatterPlotChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_XYLINE)
{
chart = new ApacheXYLineChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_RADAR || type == this.TYPE_RADAR_AREA)
{
chart = new ApacheRadarChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.TYPE_FUNNEL)
{
chart = new ApacheFunnelChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.SEMI_CIRCULAR_GAUGE)
{
chart = new ApacheSemiGaugeChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
else if(type == this.CIRCULAR_GAUGE)
{
chart = new ApacheGaugeChart(type, model, svgEmbedId,
isPerspective, legendPosition);
}
return chart;
}
ApacheChart.prototype.setPerspective = function(isPerpective)
{
this._isPerspective = isPerpective;
}
ApacheChart.prototype.clear = function()
{
var rootElem = this._rootElement;
var childNode = rootElem.firstChild;
while (childNode)
{
rootElem.removeChild(childNode);
childNode = rootElem.firstChild;
}
}
ApacheChart.prototype.draw = function()
{
if(!this._initDocument())
return;
// Initialize our gradients if necessary
if (this._gradientsUsed && !this._gradientsInitialized)
{
this.InitializeGradients();
this._gradientsInitialized = true;
}
if(this._tooltipsVisible)
{
this.ShowToolTipCallback = TrUIUtils.createCallback(this, this.ShowToolTip);
this.HideToolTipCallback = TrUIUtils.createCallback(this, this.HideToolTip);
}
this.ClickCallback = TrUIUtils.createCallback(this, this.Click);
// Note the ordering is important. The grid takes the space after the title etc.
this.DrawBorder();
this.DrawTitles();
// First just draw the label elements so that we can estimate the space requirements
this.DrawGroupLabels();
this.DrawYValueLabels();
// Now adjust margins based on the labels
this.AdjustMarginsForGroupLabels();
this.AdjustMarginsForYLabels();
// Now start drawing the graph so that it gobbles the left over space
this.DrawLegend();
this.LayoutGroupLabels();
this.LayoutYValueLabels();
this.DrawGrid();
this.DrawChartData();
this.Animate();
}
ApacheChart.prototype._initDocument = function()
{
// Get hold of the svgDocument
var svgEmbed = document.getElementById(this._svgEmbedId);
var isIE = this._isIE;
if(isIE && !ApacheChart.isASVInstalled())
{
this._displayErrorHtml(svgEmbed);
return false;
}
try
{
var svgDoc = svgEmbed.getSVGDocument();
this._rootElement = svgDoc.getElementById("chartRoot");
if(!this._rootElement) // make sure that the document is loaded
throw "not yet loaded";
this._svgDoc = svgDoc;
this._width = svgEmbed.clientWidth;
this._height = svgEmbed.clientHeight;
if(this._errorTextNode != null)
{
svgEmbed.parentNode.removeChild(this._errorTextNode);
svgEmbed.style.display = "";
}
}
catch(e)
{
this._svgCheckTotal += ApacheChart._SVGCHECK_INTERVAL;
if(this._svgCheckTotal > ApacheChart._SVGCHECK_MAX_LIMIT)
{
// We are out of our chances
this._displayErrorHtml(svgEmbed);
return false;
}
else if(null == this._errorTextNode &&
this._svgCheckTotal > ApacheChart._SVGCHECK_STATUS_LIMIT)
{
// display a status message
this._displayStatusHtml(svgEmbed);
}
if(!this._drawCallback)
this._drawCallback = TrUIUtils.createCallback(this, this.draw);
// Lets try again
window.setTimeout(this._drawCallback, ApacheChart._SVGCHECK_INTERVAL);
return false;
}
return true;
}
ApacheChart.prototype._displayStatusHtml = function(svgEmbed)
{
var errorTextNode = this._errorTextNode = document.createElement("span");
errorTextNode.innerHTML = this._statusHtml;
svgEmbed.parentNode.insertBefore(errorTextNode, svgEmbed);
svgEmbed.style.display = "none";
}
ApacheChart.prototype._displayErrorHtml = function(svgEmbed)
{
if(this._errorTextNode)
{
this._errorTextNode.innerHTML = this._errorHtml;
return;
}
else
{
var errorTextNode = this._errorTextNode = document.createElement("span");
errorTextNode.innerHTML = this._errorHtml;
svgEmbed.parentNode.insertBefore(errorTextNode, svgEmbed);
}
svgEmbed.style.display = "none";
}
ApacheChart.prototype.DrawChartData = function()
{
// no default implementation. Subclasses have to override this
}
ApacheChart.prototype.Animate = function()
{
var animateDuration = this._animDuration;
if(animateDuration > 0)
{
if(this._animCallback == null)
this._animCallback = TrUIUtils.createCallback(this, this.DoAnimation);
this._startTime = (new Date()).getTime();
this._intervalId = window.setInterval(this._animCallback, ApacheChart._ANIMATE_INTERVAL);
}
}
ApacheChart.prototype.DoAnimation = function()
{
var animateDuration = this._animDuration;
var diffTime = (new Date()).getTime() - this._startTime;
if(diffTime >= animateDuration)
{
window.clearInterval(this._intervalId);
this.SetDataAnimStep(1);
this.SetLabelsAnimStep(1);
this.SetGridAnimStep(1);
// we do not need the elements any more.
delete this._dataElems;
delete this._labelElems;
delete this._gridElems;
}
else
{
var ratio = (diffTime)/animateDuration;
this.SetDataAnimStep(ratio);
this.SetLabelsAnimStep(ratio);
this.SetGridAnimStep(ratio);
}
}
ApacheChart.prototype.SetDataAnimStep = function(ratio)
{
var animElems = this._dataElems, animCount = animElems.length;
var margins = this._margins, animHorizontal = this.AnimAlongXAxis();
// Default implementation is to make the elements appear from x axis or y axis
if(animHorizontal)
{
var marginLeft = margins.left;
for(var i = 0; i < animCount; ++i)
{
var tx = (1-ratio)*marginLeft;
animElems[i].setAttribute("transform", "translate("+tx+",0) scale("+ratio+",1)");
}
}
else
{
var marginBottom = margins.bottom, cy = (this._height - marginBottom);
for(var i = 0; i < animCount; ++i)
{
var ty = (1-ratio)*cy;
animElems[i].setAttribute("transform", "translate(0,"+ty+") scale(1,"+ratio+")");
}
}
}
ApacheChart.prototype.AnimAlongXAxis = function(ratio)
{
return false;
}
ApacheChart.prototype.SetLabelsAnimStep = function(ratio)
{
var animElems = this._labelElems, animCount = animElems.length;
// Default implementation is to make the elements fade in
for(var i = 0; i < animCount; ++i)
{
animElems[i].setAttribute("fill-opacity", ratio);
}
}
ApacheChart.prototype.SetGridAnimStep = function(ratio)
{
var animElems = this._gridElems, animCount = animElems.length;
var margins = this._margins, animHorizontal = this.AnimAlongXAxis();
// Default implementation is to make the grid appear along the x axis or y axis
if(animHorizontal)
{
var marginBottom = margins.bottom, cy = (this._height - marginBottom);
// reverse the animation for horizontal chart
for(var i = 0; i < animCount; ++i)
{
var ty = (1-ratio)*cy;
animElems[i].setAttribute("transform", "translate(0,"+ty+") scale(1,"+ratio+")");
}
}
else
{
var marginLeft = margins.left;
for(var i = 0; i < animCount; ++i)
{
var tx = (1-ratio)*marginLeft;
animElems[i].setAttribute("transform", "translate("+tx+",0) scale("+ratio+",1)");
}
}
}
ApacheChart.prototype.InitializeGradients = function()
{
var svgDoc = this._svgDoc, model = this._model, seriesColors = model.getSeriesColors(),
seriesCount = model.getSeriesLabels().length;
var gradients = svgDoc.getElementById("gradients");
ApacheChartObj.Assert(gradients, "No Gradients element in the SVG document");
var gradientElements = gradients.childNodes;
ApacheChartObj.Assert(gradients.childNodes.length>1, "No Gradient Template in the SVG document");
var gradientElement, gradientTemplate = null;
for (var i = 0; i< seriesCount; ++i)
{
gradientElement = svgDoc.getElementById("gradient"+i);
if(gradientElement == null)
{
if(gradientTemplate == null)
{
gradientTemplate = gradients.firstChild;
while(gradientTemplate.nodeType == 3 && gradientTemplate != null)
gradientTemplate = gradientTemplate.nextSibling;
}
gradientElement = gradientTemplate.cloneNode(true);
gradientElement.id = "gradient"+i;
gradients.appendChild(gradientElement);
}
var childNode = gradientElement.firstChild;
var stopIndex = 0;
while (childNode)
{
if (childNode.nodeName == "stop")
{
var color = seriesColors[i];
color = (stopIndex == 0)?color:this._getLighterColor(color);
childNode.setAttribute("stop-color",color);
this.SetStopOpacity(childNode);
if(stopIndex>=1)
break;
stopIndex++;
}
childNode = childNode.nextSibling;
}
}
}
ApacheChart.prototype.SetStopOpacity = function(stopNode)
{
// no default implementation
stopNode.setAttribute("stop-opacity", ApacheChart._DEFAULT_STOP_OPACITY);
}
ApacheChart.prototype._getLighterColor = function(color)
{
if(color.indexOf("#") >=0 )
{
color = color.substr(1);
var rVal = color.substr(0,2), gVal = color.substr(2,2), bVal = color.substr(4);
color = "#"+this._getLighterNumberStr(rVal)+this._getLighterNumberStr(gVal)+
this._getLighterNumberStr(bVal);
}
else
{
color = color.toLowerCase().replace(" ", "");
color = color.substring(4, color.length-1);
var arr = color.split(",");
color = "#"+this._getLighterNumberStr(arr[0])+this._getLighterNumberStr(arr[1])+
this._getLighterNumberStr(arr[2]);
}
return color;
}
ApacheChart.prototype._getLighterNumberStr = function(valStr)
{
var val = Math.round(parseInt(valStr, 16)*1.7);
if(val>255)
val = 255;
return ApacheChart._to_hex(val);
}
ApacheChart._to_hex = function(n)
{
var digit_array = ApacheChart._digit_array;
if(digit_array == null)
{
digit_array = ApacheChart._digit_array =
['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
}
var hex_result=''
var the_start=true;
for(var i=32;i>0;)
{
i-=4;
var one_digit=(n>>i)&0xf;
if(!the_start||one_digit!=0)
{
the_start=false;
hex_result+=digit_array[one_digit];
}
}
return ''+(hex_result==''?'0':hex_result);
}
ApacheChart.prototype.DrawBorder = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement;
var rectElem = svgDoc.getElementById("borderPrototype").cloneNode(false);
var borderSize = ApacheChart._BORDER_SIZE, stroke = borderSize/2;
rectElem.setAttribute("x", 0);
rectElem.setAttribute("y", 0);
rectElem.setAttribute("rx", stroke);
rectElem.setAttribute("ry", stroke);
rectElem.setAttribute("width", this._width-stroke);
rectElem.setAttribute("height", this._height-stroke);
rectElem.setAttribute("stroke-width", stroke);
rootElem.appendChild(rectElem);
var margins = this._margins;
margins.left += borderSize;
margins.right += borderSize;
margins.top += borderSize;
margins.bottom += borderSize;
}
ApacheChart.prototype.DrawTitles = function()
{
var model = this._model, title = model.getTitle(),
subTitle = model.getSubTitle(), footNote = model.getFootNote();
if(title)
this._drawTitleElem("titleTextPrototype", title, false);
if(subTitle)
this._drawTitleElem("subTitleTextPrototype", subTitle, false);
if(footNote)
this._drawTitleElem("footNoteTextPrototype", footNote, true);
}
ApacheChart.prototype._drawTitleElem = function(template, title, isFooter)
{
var svgDoc = this._svgDoc, rootElem = this._rootElement;
var margins = this._margins, gridWidth = (this._width - margins.left - margins.right);
var labelElems = this._labelElems, animate = (this._animDuration>0);
var textElem = svgDoc.getElementById(template).cloneNode(true);
if(animate)
{
labelElems.push(textElem);
textElem.setAttribute("fill-opacity","0");
}
textElem.firstChild.data = title;
rootElem.appendChild(textElem);
var textBBox = textElem.getBBox(), textWidth = textBBox.width, dx=margins.left;
if(isFooter && this._width > textWidth + margins.right)
dx = (this._width-textWidth)-margins.right;
if(!isFooter && gridWidth > textWidth)
dx = (gridWidth-textWidth)/2+margins.left;
textElem.setAttribute("x",dx);
if(isFooter)
{
textElem.setAttribute("y",this._height-margins.bottom);
margins.bottom += textBBox.height+ApacheChart._TEXT_MARGIN;
}
else
{
margins.top += textBBox.height;
textElem.setAttribute("y",margins.top);
margins.top += ApacheChart._TEXT_MARGIN;
}
}
ApacheChart.prototype.DrawGroupLabels = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var container = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
this._hLabelContainer = container;
var groupLabels = model.getGroupLabels(), vLineCount = groupLabels.length;
var labelElem, labelElems = this._labelElems, animate = (this._animDuration>0);
var labelText, gLabelElems = this._groupLabelElems;
for (var i = 0; i< vLineCount; ++i)
{
// draw the horizontal label
if(i==0)
{
labelElem = svgDoc.getElementById("groupLabelPrototype");
}
labelText = groupLabels[i];
if(!labelText)
continue;
labelElem = labelElem.cloneNode(true);
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
labelElem.firstChild.data = labelText;
container.appendChild(labelElem);
gLabelElems[i] = labelElem;
}
rootElem.appendChild(container);
}
ApacheChart.prototype.LayoutGroupLabels = function()
{
var model = this._model, margins = this._margins, marginLeft = margins.left;
var container = this._hLabelContainer, childNodes = container.childNodes;
if(childNodes.length == 0)
return;
if(this._isPerspective)
marginLeft += ApacheChart._XOFFSET_PERSPECTIVE;
var gridWidth = (this._width - marginLeft - margins.right);
var isCenterAligned = this.IsGroupLabelCentered();
var groupLabels = model.getGroupLabels(), vLineCount = groupLabels.length;
var labelElem, groupWidth = gridWidth/(isCenterAligned?vLineCount:vLineCount-1);
var dx = 0, dy = this._height - margins.bottom+container.getBBox().height+ApacheChart._TEXT_MARGIN;
var gLabelElems = this._groupLabelElems;
for (var i = 0; i< vLineCount; ++i)
{
labelElem = gLabelElems[i];
if(!labelElem)
continue;
labelElem.setAttribute("y", dy);
var textWidth = labelElem.getBBox().width;
if(isCenterAligned)
{
if(groupWidth > textWidth)
dx = (groupWidth-textWidth)/2;
else
dx = 2;
}
else
{
dx = (-textWidth)/2;
if(this._isPerspective)
dx -= ApacheChart._XOFFSET_PERSPECTIVE;
}
labelElem.setAttribute("x", marginLeft+dx+i*groupWidth);
}
}
/**
* Indicates if the group label should be center aligned or edge aligned
* @return true(String) indicates center aligned, false indicates it is edge aligned
*/
ApacheChart.prototype.IsGroupLabelCentered = function()
{
return true;
}
ApacheChart.prototype.AdjustMarginsForGroupLabels = function()
{
var container = this._hLabelContainer;
if(container && container.childNodes.length > 0)
{
this._margins.bottom += container.getBBox().height+ApacheChart._TEXT_MARGIN;
var isCentered = this.IsGroupLabelCentered();
if(!isCentered)
{
var textWidth = container.lastChild.getBBox().width;
if(textWidth/2> this._margins.right)
this._margins.right = textWidth/2;
}
}
}
ApacheChart.prototype.DrawLegend = function()
{
var legendPosition = this._legendPosition;
if(legendPosition == ApacheChart.LEGEND_LOCATION_NONE)
{
return;
}
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var gradientsUsed = this._gradientsUsed;
var seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length,
seriesColors = model.getSeriesColors();
var labelElem, rectElem, legendRectHeight,
legendGroup = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var labelElems = this._labelElems, animate = (this._animDuration>0);
rootElem.appendChild(legendGroup);
if(this._isPerspective)
{
marginLeft += ApacheChart._XOFFSET_PERSPECTIVE;
}
var gridWidth = (this._width - marginLeft - margins.right),
gridHeight = (this._height - marginTop - margins.bottom);
if(animate)
{
labelElems.push(legendGroup);
legendGroup.setAttribute("fill-opacity","0");
}
var dx = 0, dy = 0, tx = marginLeft, ty = this._height - margins.bottom;
var drawSideWays = (legendPosition == ApacheChart.LEGEND_LOCATION_START ||
legendPosition == ApacheChart.LEGEND_LOCATION_END)
for (var i = 0; i < seriesCount; ++i)
{
if(i == 0)
{
labelElem = svgDoc.getElementById("legendTextPrototype");
rectElem = svgDoc.getElementById("legendRectPrototype");
legendRectHeight = parseInt(rectElem.getAttribute("height"));
}
if(drawSideWays)
dx = 0;
rectElem = rectElem.cloneNode(false);
rectElem.setAttribute("x", dx);
rectElem.setAttribute("y", dy-legendRectHeight);
if(gradientsUsed)
rectElem.setAttribute("fill", "url(#gradient"+i+")");
else
rectElem.setAttribute("fill", seriesColors[i]);
rectElem.setAttribute("stroke", "#000000");
// TODO: Legend elements should fire on click event
//rectElem.setAttribute("onclick", "parent."+onclickStrings[i]);
legendGroup.appendChild(rectElem);
dx += 1.5*legendRectHeight;
labelElem = labelElem.cloneNode(true);
labelElem.setAttribute("x", dx);
labelElem.setAttribute("y", dy);
labelElem.firstChild.data = seriesLabels[i];
legendGroup.appendChild(labelElem);
// TODO: Legend elements should fire on click event
//labelElem.setAttribute("onclick", "parent."+onclickStrings[i]);
if(!drawSideWays)
dx += labelElem.getBBox().width+legendRectHeight;
else
dy += 1.5*legendRectHeight;
if(i == 0 && !drawSideWays)
{
var rect = labelElem.getBBox();
if(legendPosition == ApacheChart.LEGEND_LOCATION_TOP)
{
ty = this.SetLegendTopAdjustment(margins.top+rect.height);
margins.top += rect.height+ApacheChart._TEXT_MARGIN;
}
else
{
ty = this.SetLegendBottomAdjustment(ty);
margins.bottom += rect.height+ApacheChart._TEXT_MARGIN;
}
}
}
if(!drawSideWays && gridWidth > dx)
tx = (gridWidth-dx)/2+marginLeft;
if(drawSideWays)
{
var lBBox = legendGroup.getBBox();
if(legendPosition == ApacheChart.LEGEND_LOCATION_START)
{
tx = this.SetLegendLeftAdjustment(margins.left);
margins.left += lBBox.width+ApacheChart._TEXT_MARGIN;
}
else
{
margins.right += lBBox.width+ApacheChart._TEXT_MARGIN;
tx = this._width - margins.right + ApacheChart._TEXT_MARGIN;
tx = this.SetLegendRightAdjustment(tx);
}
if(gridHeight > dy)
ty = (gridHeight-lBBox.height)/2+marginTop;
else
ty = gridHeight+marginTop-lBBox.height;
}
legendGroup.setAttribute("transform", "translate("+tx+","+ty+")");
}
/**
* Adjusts the legend location when it is at the top
* @param ty(int) the original y location of the legend
*/
ApacheChart.prototype.SetLegendTopAdjustment = function(ty)
{
// By default we need not adjust anything
return ty;
}
/**
* Adjusts the legend location when it is at the bottom
* @param ty(int) the original y location of the legend
*/
ApacheChart.prototype.SetLegendBottomAdjustment = function(ty)
{
var container = this._hLabelContainer;
if(container && container.childNodes.length > 0)
{
ty += container.getBBox().height+ApacheChart._TEXT_MARGIN;
}
return ty;
}
/**
* Adjusts the legend location when it is at the Left
* @param tx(int) the original x location of the legend
*/
ApacheChart.prototype.SetLegendLeftAdjustment = function(tx)
{
var container = this._vLabelContainer;
if(container)
{
tx -= container.getBBox().width+ApacheChart._TEXT_MARGIN;
}
return tx;
}
/**
* Adjusts the legend location when it is at the Right
* @param tx{int} the original x location of the legend
*/
ApacheChart.prototype.SetLegendRightAdjustment = function(tx)
{
// By default we need not adjust anything
return tx;
}
ApacheChart.prototype.DrawGrid = function()
{
if(this._isPerspective)
this.DrawPerspectiveGrid();
else
this.Draw2DGrid();
}
ApacheChart.prototype.Draw2DGrid = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var gridElems = this._gridElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var gradientsUsed = this._gradientsUsed;
var rectElem = svgDoc.getElementById("gridRectPrototype").cloneNode(false);
rectElem.setAttribute("x", margins.left);
rectElem.setAttribute("y", (marginTop));
rectElem.setAttribute("width", gridWidth);
rectElem.setAttribute("height", gridHeight);
if(gradientsUsed)
rectElem.setAttribute("fill", "url(#gridGradient)");
this._rootElement.appendChild(rectElem);
var pathElem = svgDoc.getElementById("gridPathPrototype").cloneNode(false);
if(animate)
{
gridElems.push(pathElem);
pathElem.setAttribute("transform", "scale(0.00001,1)");
}
var sb = new ApacheChartBuffer(), vLineCount = this.GetVLineCount(), hLineCount = this.GetHLineCount();
// horizontal lines
for (var i = 0; i< hLineCount-1; ++i)
{
sb.append("M").append(marginLeft).append(",").append((i+1)*gridHeight/hLineCount+marginTop);
sb.append("h").append(gridWidth);
}
// vertical lines
for (var i = 0; i< vLineCount-1; ++i)
{
sb.append("M").append(marginLeft+((i+1)*gridWidth/vLineCount)).append(",").append(marginTop);
sb.append("v").append(gridHeight);
}
pathElem.setAttribute("d", sb.toString());
pathElem.removeAttribute("id");
this._rootElement.appendChild(pathElem);
}
ApacheChart.prototype.GetVLineCount = function()
{
var xMajorCount = this._xMajorGridCount;
if(xMajorCount >= 0)
return xMajorCount;
else
return this._model.getGroupLabels().length;
}
ApacheChart.prototype.GetHLineCount = function()
{
return this._yMajorGridCount;
}
ApacheChart.prototype.DrawPerspectiveGrid = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var gridElems = this._gridElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var rectElem = svgDoc.getElementById("gridRectPrototype").cloneNode(false);
var gradientsUsed = this._gradientsUsed;
rectElem.setAttribute("x", marginLeft+ApacheChart._XOFFSET_PERSPECTIVE);
rectElem.setAttribute("y", marginTop);
rectElem.setAttribute("width", (gridWidth));
rectElem.setAttribute("height", (gridHeight));
if(gradientsUsed)
rectElem.setAttribute("fill", "url(#gridGradient)");
rectElem.removeAttribute("id");
this._rootElement.appendChild(rectElem);
var sb = new ApacheChartBuffer();
var pathElem = svgDoc.getElementById("gridPath3dRectPrototype").cloneNode(false);
sb.append("M").append(marginLeft+xOffset).append(",").append(marginTop);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("v").append(gridHeight);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("m").append(gridWidth).append(",").append(0);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("h").append(-gridWidth);
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gridGradient)");
pathElem.setAttribute("d", sb.toString());
pathElem.removeAttribute("id");
this._rootElement.appendChild(pathElem);
pathElem = svgDoc.getElementById("gridPathPrototype").cloneNode(false);
if(animate)
{
pathElem.setAttribute("transform", "scale(0.00001,1)");
gridElems.push(pathElem);
}
var vLineCount = this.GetVLineCount(), hLineCount = this.GetHLineCount();
sb = new ApacheChartBuffer();
// horizontal lines
for (var i = 0; i< hLineCount-1; ++i)
{
sb.append("M").append(marginLeft).append(",").append((i+1)*gridHeight/hLineCount+marginTop+yOffset);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("h").append(gridWidth);
}
// vertical lines
for (var i = 0; i< vLineCount-1; ++i)
{
sb.append("M").append(marginLeft+xOffset+((i+1)*gridWidth/vLineCount)).append(",").append(marginTop);
sb.append("v").append(gridHeight);
sb.append("l").append(-xOffset).append(",").append(yOffset);
}
pathElem.setAttribute("d", sb.toString());
this._rootElement.appendChild(pathElem);
}
ApacheChart.prototype.DrawYValueLabels = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var container = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
this._vLabelContainer = container;
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var labelElems = this._labelElems, animate = (this._animDuration>0);
var labelElem = svgDoc.getElementById("yLabelPrototype").cloneNode(true);
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
labelElem.firstChild.data = this._formatValue(minValue);
container.appendChild(labelElem);
labelElem = labelElem.cloneNode(true);
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
labelElem.firstChild.data = this._formatValue(maxValue);
container.appendChild(labelElem);
var hLineCount = this._yMajorGridCount;
// horizontal lines
for (var i = 0; i< hLineCount-1; ++i)
{
var value = ((maxValue-minValue)*(i+1)/hLineCount) + minValue;
labelElem = labelElem.cloneNode(true);
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
labelElem.firstChild.data = this._formatValue(value);
container.appendChild(labelElem);
}
rootElem.appendChild(container);
}
ApacheChart.prototype._formatValue = function(value)
{
// Initialize the decimal separtor
var decimalSep = this._decimalSep;
if(decimalSep == null)
{
var symbols = window.getLocaleSymbols?getLocaleSymbols():null;
if (symbols)
{
this._decimalSep = symbols.getDecimalSeparator();
}
else
this._decimalSep = ".";
decimalSep = this._decimalSep;
}
value = value.toFixed(this._maxPrecision);
value = value.toString();
if(value.indexOf(decimalSep) == -1)
{
value = value.replace(".", decimalSep);
}
return value;
}
ApacheChart.prototype.AdjustMarginsForYLabels = function()
{
var container = this._vLabelContainer;
if(container && container.childNodes.length > 0)
this._margins.left += container.getBBox().width+ApacheChart._TEXT_MARGIN;
}
ApacheChart.prototype.LayoutYValueLabels = function()
{
var model = this._model, margins = this._margins;
var marginLeft = margins.left, marginTop = margins.top;
var container = this._vLabelContainer, childNodes = container.childNodes;
var gridHeight = (this._height - marginTop - margins.bottom);
if(this._isPerspective)
gridHeight -= ApacheChart._YOFFSET_PERSPECTIVE;
var bBox = container.getBBox(), textHeight = bBox.height;
this.SetVerticalLabelAt(childNodes.item(0), gridHeight+marginTop,
marginLeft, textHeight);
this.SetVerticalLabelAt(childNodes.item(1), marginTop,
marginLeft, textHeight);
var hLineCount = this._yMajorGridCount;
// horizontal lines
for (var i = 0; i< hLineCount-1; ++i)
{
this.SetVerticalLabelAt(childNodes.item(i+2),
(hLineCount -i -1)*gridHeight/hLineCount+marginTop,
marginLeft, textHeight);
}
}
ApacheChart.prototype.SetVerticalLabelAt = function(
labelElem, y, marginLeft, textHeight)
{
if(this._isPerspective)
y += ApacheChart._YOFFSET_PERSPECTIVE;
// readjust to right align
var labelMargin = ApacheChart._TEXT_MARGIN,
textLength = labelElem.getBBox().width, dx = labelMargin;
if(marginLeft>textLength+labelMargin)
dx = marginLeft-textLength-labelMargin;
labelElem.setAttribute("x", dx);
labelElem.setAttribute("y", y+textHeight/2);
}
ApacheChart.prototype.DrawGroupLabelTitle = function(
label, container, labelElem, dx, dy,
quadWidth, quadHeight)
{
if(!label)
return quadHeight;
var labelElems = this._labelElems, animate = (this._animDuration>0);
labelElem.setAttribute("y", dy+quadHeight);
labelElem.firstChild.data = label;
container.appendChild(labelElem);
var rect = labelElem.getBBox();
var textWidth = rect.width;
if(quadWidth > textWidth)
dx += (quadWidth-textWidth)/2;
else
dx += 2;
labelElem.setAttribute("x", dx);
if(animate)
labelElems.push(labelElem);
quadHeight -= rect.height+ApacheChart._TEXT_MARGIN;
return quadHeight;
}
ApacheChart.prototype.ShowToolTip = function(e)
{
if (this._toolTipVisible)
return;
var model = this._model, seriesColors = model.getSeriesColors();
var toolTip = this.getToolTip();
if (toolTip == null)
{
toolTip = this._svgDoc.getElementById("toolTip").cloneNode(true);
this.setToolTip(toolTip);
this._rootElement.appendChild(toolTip);
}
toolTip.style.setProperty("visibility","visible","");
var circleElem = toolTip.firstChild.nextSibling;
var boundingRectElem = circleElem.nextSibling.nextSibling;
this.FillToolTipData(boundingRectElem, circleElem, e);
var ttBBox = toolTip.getBBox();
var pt = this.GetToolTipLocation(e, ttBBox);
var dx = pt.x, dy = pt.y;
if(dx + ttBBox.width > this._width)
{
dx -= ttBBox.width;
circleElem.setAttribute("cx",boundingRectElem.getBBox().width);
}
else
{
circleElem.setAttribute("cx",0);
}
if(dy - ttBBox.height < 0)
{
dy += ttBBox.height;
circleElem.setAttribute("cy",0);
}
else
{
circleElem.setAttribute("cy",boundingRectElem.getBBox().height);
}
if(this._isPerspective && this._type != ApacheChart.TYPE_PIE)
dy += ApacheChart._YOFFSET_PERSPECTIVE/2
toolTip.setAttribute("transform","translate("+dx+","+dy+")");
this._toolTipVisible = true;
}
ApacheChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
var targetBBox = e.target.getBBox();
return {x:(targetBBox.x+targetBBox.width/2), y:(targetBBox.y - ttBBox.height)};
}
ApacheChart.prototype.GetChartEvent = function(e)
{
var evtTarget = e.target;
var i = parseInt(evtTarget.getAttribute("yValueIndex")),
j = parseInt(evtTarget.getAttribute("seriesIndex"));
var model = this._model, yValues = model.getYValues();
return new ApacheChartEvent([j],[i], [yValues[i][j]],null);
}
ApacheChart.prototype.FillToolTipData = function(boundingRectElem, circleElem, e)
{
var chartEvent = this.GetChartEvent(e);
var j = chartEvent.getSeriesIndices()[0];
var model = this._model, groupLabels = model.getGroupLabels(),
seriesLabels = model.getSeriesLabels(),
yValues = chartEvent.getYValues();
//top label
var textElem = boundingRectElem.nextSibling.nextSibling;
textElem.firstChild.data = seriesLabels[j];
var labelWidth = textElem.getBBox().width;
//actual value
textElem = textElem.nextSibling.nextSibling;
textElem.firstChild.data = this._formatValue(yValues[0]);
var dataWidth = textElem.getBBox().width;
// leave a clearance on either end of the text
var xMargin = ApacheChart._TEXT_MARGIN, dx = xMargin;
if (labelWidth > dataWidth)
dx = (labelWidth-dataWidth)/2+xMargin;
textElem.setAttribute("x",dx);
var rectWidth = Math.max(labelWidth,dataWidth)+2*xMargin;
boundingRectElem.setAttribute("width",rectWidth);
boundingRectElem.setAttribute("stroke", seriesColors[j]);
circleElem.setAttribute("stroke",seriesColors[j]);
}
ApacheChart.prototype.HideToolTip = function(e)
{
var toolTip = this.getToolTip();
if(toolTip)
toolTip.style.setProperty("visibility","hidden","");
this._toolTipVisible = false;
}
ApacheChart.prototype.Click = function(e)
{
var chartEvent = this.GetChartEvent(e);
var formName = this._formName;
if(formName !=null)
{
var svgEmbed = document.getElementById(this._svgEmbedId);
var sourceId = svgEmbed.parentNode.id;
var chartValue ={ 'event': 'chartDrillDown',
'source':sourceId,
'value':chartEvent.marshall()};
if(this._partialSubmit)
{
_submitPartialChange(formName,'0',chartValue);
}
else
{
submitForm(formName,'0',chartValue);
}
}
else
alert(chartEvent);
}
////////////////////////////////////////////////////////////////////
// Bar Chart subclass
////////////////////////////////////////////////////////////////////
function ApacheBarChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheBarChart);
ApacheBarChart.prototype.DrawChartData = function()
{
var isCombo = this._type == ApacheChart.TYPE_BAR_LINE_COMBO;
var isPerspective = this._isPerspective;
if(isPerspective)
this._drawPerspectiveBars(isCombo);
else
this._drawBars(isCombo);
// delegate to the line chart for combos
if(isCombo)
{
if(isPerspective)
this.__drawPerspectiveLines = ApacheLineChart.prototype.__drawPerspectiveLines;
else
this.__drawLines = ApacheLineChart.prototype.__drawLines;
ApacheLineChart.prototype.DrawChartData.call(this, isCombo);
}
}
ApacheBarChart.prototype._drawBars = function(isCombo)
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var rectElem = svgDoc.getElementById("barRectPrototype");
var barItemPadding = ApacheBarChart._BARITEM_PADDING;
var isStacked = (this._type == ApacheChart.TYPE_VBAR_STACKED);
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var barDivider = isStacked?1:(isCombo?Math.ceil(seriesCount/2): seriesCount);
var yValueCount = yValues.length;
var barWidth = (gridWidth/Math.max(yValueCount,groupCount)-2*barItemPadding)/barDivider;
var dx = marginLeft, dy, barHeight, stackBase = minValue;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(1,0.00001)";
for (var i = 0; i< yValueCount; ++i)
{
dx += barItemPadding;
dy = gridHeight + marginTop;
for (var j = 0; j < seriesCount; ++j)
{
// for combo charts we draw every alternate(even) bar.
if(isCombo && j%2>0)
continue;
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
if(isStacked)
stackBase = (j==0?minValue:0);
rectElem = rectElem.cloneNode(false);
if(animate)
{
dataElems.push(rectElem);
rectElem.setAttribute("transform",defaultTransform);
}
rectElem.setAttribute("x", dx);
barHeight = gridHeight*(yValues[i][j]-stackBase)/(maxValue-minValue);
if(isStacked)
dy -= barHeight;
else
dy = gridHeight + marginTop - barHeight;
rectElem.setAttribute("y", dy);
rectElem.setAttribute("width", barWidth);
rectElem.setAttribute("height", barHeight);
if(gradientsUsed)
rectElem.setAttribute("fill", "url(#gradient"+j+")");
else
rectElem.setAttribute("fill", seriesColors[j]);
rectElem.setAttribute("stroke", seriesColors[j]);
rectElem.setAttribute("stroke-width", 1);
rectElem.setAttribute("yValueIndex", i);
rectElem.setAttribute("seriesIndex", j);
if(this._tooltipsVisible)
{
rectElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
rectElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
rectElem.addEventListener("click",this.ClickCallback,false);
rootElem.appendChild(rectElem);
if(!isStacked)
dx += barWidth;
}
if(isStacked)
dx += barWidth;
dx += barItemPadding;
}
}
ApacheBarChart.prototype._drawPerspectiveBars = function(isCombo)
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var pathElem = svgDoc.getElementById("barPathPrototype");
var barItemPadding = ApacheBarChart._BARITEM_PADDING;
var isStacked = (this._type == ApacheChart.TYPE_VBAR_STACKED);
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var seriesBars = isCombo?Math.ceil(seriesCount/2): seriesCount, barWidth;
var yValueCount = yValues.length;
if(isStacked)
barWidth = gridWidth/Math.max(yValueCount,groupCount)-2*barItemPadding;
else
barWidth = (gridWidth/Math.max(yValueCount,groupCount) -2*barItemPadding - (seriesBars)*barItemPadding)/seriesBars;
var dx = marginLeft, dy, barHeight, stackBase = minValue;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(1, 0.00001)";
for (var i = 0; i< yValueCount; ++i)
{
dx += barItemPadding;
dy = gridHeight + marginTop+yOffset;
for (var j = 0; j < seriesCount; ++j)
{
// for combo charts we draw every alternate(even) bar.
if(isCombo && j%2>0)
continue;
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
if(isStacked)
stackBase = (j==0?minValue:0);
barHeight = gridHeight*(yValues[i][j]-stackBase)/(maxValue-minValue);
if(isStacked)
dy -= barHeight;
else
dy = gridHeight + yOffset + marginTop - barHeight;
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
var sb = new ApacheChartBuffer();
sb.append("M").append(dx).append(",").append(dy);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("h").append(barWidth);
sb.append("v").append(barHeight);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("v").append(-barHeight);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("h").append(-barWidth);
sb.append("v").append(barHeight);
sb.append("h").append(barWidth);
sb.append("v").append(-barHeight);
pathElem.setAttribute("stroke", seriesColors[j]);
pathElem.setAttribute("stroke-width", 1);
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+j+")");
else
pathElem.setAttribute("fill", seriesColors[j]);
pathElem.setAttribute("d", sb.toString());
pathElem.setAttribute("yValueIndex", i);
pathElem.setAttribute("seriesIndex", j);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
rootElem.appendChild(pathElem);
if(!isStacked)
{
dx += barWidth;
dx += barItemPadding;
}
}
if(isStacked)
dx += barWidth;
dx += barItemPadding;
}
}
ApacheBarChart.prototype.ShowToolTip = function(e)
{
if(this._type == ApacheChart.TYPE_BAR_LINE_COMBO)
{
var i = parseInt(e.target.getAttribute("seriesIndex"));
if(i%2>0)
{
try
{
// Maybe we need a generic framework for combos so that we can delegate.
// Till that time...
this.GetToolTipLocation = ApacheLineChart.prototype.GetToolTipLocation;
this.FillToolTipData = ApacheLineChart.prototype.FillToolTipData;
this.GetChartEvent = ApacheLineChart.prototype.GetChartEvent;
ApacheLineChart.prototype.ShowToolTip.call(this, e);
}
finally
{
// restore
this.GetToolTipLocation = ApacheBarChart.prototype.GetToolTipLocation;
this.FillToolTipData = ApacheBarChart.prototype.FillToolTipData;
this.GetChartEvent = ApacheBarChart.prototype.GetChartEvent;
}
return;
}
}
ApacheBarChart.superclass.ShowToolTip.call(this, e);
}
// number of pixels on either side of the bar item
ApacheBarChart._BARITEM_PADDING = 2;
////////////////////////////////////////////////////////////////////
// Bar chart subclass
////////////////////////////////////////////////////////////////////
function ApacheHBarChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheHBarChart);
ApacheHBarChart.prototype.DrawChartData = function()
{
if(this._isPerspective)
this._drawPerspectiveBars();
else
this._drawBars();
}
ApacheHBarChart.prototype.AnimAlongXAxis = function()
{
// horizontal bar animates around x axis
return true;
}
ApacheHBarChart.prototype.DrawYValueLabels = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var container = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
// Since the horizontal bar chart is flipped Y labels are horizontal
this._vLabelContainer = container;
var labelElem = svgDoc.getElementById("groupLabelPrototype");
var labelElems = this._labelElems, animate = (this._animDuration>0);
var groupLabels = model.getGroupLabels(), hLineCount = groupLabels.length;
var labelText, gLabelElems = this._groupLabelElems;
// horizontal lines
for (var i = 0; i< hLineCount; ++i)
{
labelText = groupLabels[i];
if(!labelText)
continue;
labelElem = labelElem.cloneNode(true);
labelElem.firstChild.data = labelText;
container.appendChild(labelElem);
gLabelElems[i] = labelElem;
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
}
rootElem.appendChild(container);
}
ApacheHBarChart.prototype.LayoutYValueLabels = function()
{
var model = this._model, margins = this._margins,
marginLeft = margins.left, marginTop = margins.top;
var gridHeight = (this._height - marginTop - margins.bottom);
if(this._isPerspective)
gridHeight -= ApacheChart._YOFFSET_PERSPECTIVE;
var container = this._vLabelContainer, childNodes = container.childNodes;
if(childNodes.length == 0)
return;
var labelElem, bBox = container.getBBox(), textHeight = bBox.height;
var groupLabels = model.getGroupLabels(), hLineCount = groupLabels.length;
var gLabelElems = this._groupLabelElems;
// horizontal lines
for (var i = 0; i< hLineCount; ++i)
{
labelElem = gLabelElems[i];
if(!labelElem)
continue;
this.SetVerticalLabelAt(labelElem,
(hLineCount -i)*gridHeight/hLineCount+marginTop-(gridHeight/(2*hLineCount)),
marginLeft, textHeight);
}
}
ApacheHBarChart.prototype.IsGroupLabelCentered = function()
{
return false;
}
ApacheHBarChart.prototype.DrawGroupLabels = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var vLineCount = this._yMajorGridCount;
var container = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
// Since the horizontal bar chart is flipped group labels are vertical
this._hLabelContainer = container;
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var labelElem = svgDoc.getElementById("yLabelPrototype");
var value, labelElems = this._labelElems, animate = (this._animDuration>0);
for (var i = 0; i< vLineCount+1; ++i)
{
// draw the horizontal label
labelElem = labelElem.cloneNode(true);
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
if(i==0)
value = minValue;
else if(i==vLineCount)
value = maxValue;
else
value = (((maxValue-minValue)*(i)/vLineCount) + minValue);
labelElem.firstChild.data = this._formatValue(value);
container.appendChild(labelElem);
}
rootElem.appendChild(container);
}
ApacheHBarChart.prototype.LayoutGroupLabels = function()
{
var model = this._model, margins = this._margins, marginLeft = margins.left;
var gridWidth = (this._width - marginLeft - margins.right);
var container = this._hLabelContainer, childNodes = container.childNodes;
if(this._isPerspective)
gridWidth -= ApacheChart._XOFFSET_PERSPECTIVE;
var vLineCount = this._yMajorGridCount;
var labelElem, yValWidth = gridWidth/vLineCount;
var bBox = container.getBBox();
var dx = 0, dy = this._height - margins.bottom + bBox.height+ApacheChart._TEXT_MARGIN;
var labelElems = this._labelElems, animate = (this._animDuration>0);
for (var i = 0; i< vLineCount+1; ++i)
{
// draw the horizontal label
labelElem = childNodes.item(i);
labelElem.setAttribute("y", dy);
var textWidth = labelElem.getBBox().width;
labelElem.setAttribute("x", marginLeft-textWidth/2+i*yValWidth);
}
}
ApacheHBarChart.prototype.GetVLineCount = function()
{
return this._yMajorGridCount;
}
ApacheHBarChart.prototype.GetHLineCount = function()
{
var xMajorCount = this._xMajorGridCount;
if(xMajorCount >= 0)
return xMajorCount;
else
return this._model.getGroupLabels().length;
}
ApacheHBarChart.prototype._drawBars = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var rectElem = svgDoc.getElementById("barRectPrototype");
var barItemPadding = ApacheBarChart._BARITEM_PADDING;
var isStacked = (this._type == ApacheChart.TYPE_HBAR_STACKED);
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var barDivider = isStacked?1:seriesCount, stackBase = minValue;
var yValueCount = yValues.length;
var barHeight = (gridHeight/Math.max(yValueCount,groupCount)-2*barItemPadding)/barDivider;
var dx = marginLeft, dy=gridHeight+marginTop, barWidth;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(0.00001,1)";
for (var i = 0; i< yValueCount; ++i)
{
dy -= barItemPadding;
dx = marginLeft;
for (var j = 0; j < seriesCount; ++j)
{
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
if(isStacked)
stackBase = (j==0?minValue:0);
rectElem = rectElem.cloneNode(false);
if(animate)
{
dataElems.push(rectElem);
rectElem.setAttribute("transform",defaultTransform);
}
rectElem.setAttribute("x", dx);
barWidth = gridWidth*(yValues[i][j]-stackBase)/(maxValue-minValue);
if(isStacked)
dx += barWidth;
rectElem.setAttribute("y", dy-barHeight);
rectElem.setAttribute("width", barWidth);
rectElem.setAttribute("height", barHeight);
if(gradientsUsed)
rectElem.setAttribute("fill", "url(#gradient"+j+")");
else
rectElem.setAttribute("fill", seriesColors[j]);
rectElem.setAttribute("stroke", seriesColors[j]);
rectElem.setAttribute("stroke-width", 1);
rectElem.setAttribute("yValueIndex", i);
rectElem.setAttribute("seriesIndex", j);
if(this._tooltipsVisible)
{
rectElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
rectElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
rectElem.addEventListener("click",this.ClickCallback,false);
rootElem.appendChild(rectElem);
if(!isStacked)
dy -= barHeight;
}
if(isStacked)
dy -= barHeight;
dy -= barItemPadding;
}
}
ApacheHBarChart.prototype._drawPerspectiveBars = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var pathElem = svgDoc.getElementById("barPathPrototype");
var barItemPadding = ApacheBarChart._BARITEM_PADDING;
var isStacked = (this._type == ApacheChart.TYPE_HBAR_STACKED);
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var yValueCount = yValues.length;
var barHeight, stackBase = minValue;
if(isStacked)
barHeight = gridHeight/Math.max(yValueCount,groupCount)-2*barItemPadding;
else
barHeight = (gridHeight/Math.max(yValueCount,groupCount)-2*barItemPadding - (seriesCount)*barItemPadding)/seriesCount;
var dx = marginLeft, dy=gridHeight+marginTop+yOffset, barWidth;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(0.00001,1)";
for (var i = 0; i< yValueCount; ++i)
{
dy -= barItemPadding;
dx = marginLeft;
for (var j = 0; j < seriesCount; ++j)
{
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
if(isStacked)
stackBase = (j==0?minValue:0);
barWidth = gridWidth*(yValues[i][j]-stackBase)/(maxValue-minValue);
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
var sb = new ApacheChartBuffer();
sb.append("M").append(dx).append(",").append(dy);
sb.append("h").append(barWidth);
sb.append("v").append(-barHeight);
sb.append("h").append(-barWidth);
sb.append("v").append(barHeight);
sb.append("M").append(dx).append(",").append(dy-barHeight);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("h").append(barWidth);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("z");
sb.append("M").append(dx+barWidth).append(",").append(dy);
sb.append("v").append(-barHeight);
sb.append("l").append(xOffset).append(",").append(-yOffset);
sb.append("v").append(barHeight);
sb.append("z");
pathElem.setAttribute("stroke", seriesColors[j]);
pathElem.setAttribute("stroke-width", 1);
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+j+")");
else
pathElem.setAttribute("fill", seriesColors[j]);
pathElem.setAttribute("d", sb.toString());
pathElem.setAttribute("yValueIndex", i);
pathElem.setAttribute("seriesIndex", j);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
rootElem.appendChild(pathElem);
if(isStacked)
dx += barWidth;
else
{
dy -= barHeight;
dy -= barItemPadding;
}
}
if(isStacked)
dy -= barHeight;
dy -= barItemPadding;
}
}
////////////////////////////////////////////////////////////////////
// Pie chart subclass
////////////////////////////////////////////////////////////////////
function ApachePieChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApachePieChart);
ApachePieChart.prototype.Init = function(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
ApachePieChart.superclass.Init.call(this, type, model, svgEmbedId,
isPerspective, legendPosition);
//this._pieAnimAngles = undefined;
//this._pieAnimRadii = undefined;
}
ApachePieChart.prototype.DrawChartData = function()
{
var rootElem = this._rootElement;
// calculate the number of rows and columns
var model = this._model, yValues = model.getYValues(), yValueCount = yValues.length;
var groupLabels = model.getGroupLabels(), groupCount = groupLabels?groupLabels.length:1;
var nCols = Math.ceil(Math.sqrt(yValueCount)), nRows = Math.round(Math.sqrt(yValueCount));
var labelElem = this._svgDoc.getElementById("groupLabelPrototype");
var margins = this._margins, dx=margins.left, dy=margins.top;
var quadWidth = (this._width - margins.left - margins.right)/nCols;
var animate = (this._animDuration>0), isPerspective = this._isPerspective;
var pieAnimRadii, vGap = 2*ApacheChart._TEXT_MARGIN;
var quadHeight = (this._height - margins.top - margins.bottom - (nRows-1)*vGap)/nRows;
if(animate)
{
this._pieAnimAngles = [];
pieAnimRadii = this._pieAnimRadii = [];
}
for(var i = 0; i= yValueCount)
break;
var groupLabel = (iGroup == -1)?null:groupLabels[iGroup];
var pieContainer = rootElem.cloneNode(false);
rootElem.appendChild(pieContainer);
var newHeight = this.DrawGroupLabelTitle(groupLabel, rootElem,
labelElem.cloneNode(true), dx, dy,
quadWidth, quadHeight);
var newWidth = quadWidth - 2*ApacheChart._TEXT_MARGIN;
var cx= dx+quadWidth/2+ApacheChart._TEXT_MARGIN, cy = dy+newHeight/2;
if(animate)
{
pieAnimRadii.push(Math.max(cx, cy));
}
if(isPerspective)
{
this._draw3DPies(pieContainer, newWidth, newHeight, iGroup);
// The chart is draw with the center at 0 so we need to compensate for it.
pieContainer.setAttribute("transform",
"translate("+cx+","+cy+") scale(1.0,0.707)");
}
else
{
this._drawPies(pieContainer, newWidth, newHeight, iGroup);
pieContainer.setAttribute("transform",
"translate("+cx+","+cy+")");
}
dx +=quadWidth;
}
dx=margins.left;
dy +=quadHeight+vGap;
}
}
ApachePieChart.prototype.ComputeMinMaxValues = function()
{
}
ApachePieChart.prototype.DrawGroupLabels = function()
{
}
ApachePieChart.prototype.LayoutGroupLabels = function()
{
}
ApachePieChart.prototype.DrawGrid = function()
{
}
ApachePieChart.prototype.DrawYValueLabels = function()
{
}
ApachePieChart.prototype.LayoutYValueLabels = function()
{
}
ApachePieChart.prototype.SetDataAnimStep = function(ratio)
{
var pieAnimRadii = this._pieAnimRadii, pieAnimAngles = this._pieAnimAngles,
isPerspective = this._isPerspective, agleIndex = 0, elemIndex = 0;
var animElems = this._dataElems, chartCount = pieAnimRadii.length;
var model = this._model, yValues = model.getYValues();
// We are animating parependiculat to the tangent to the middle of the pie
for(var i = 0; i < chartCount; ++i)
{
var nPies = yValues[i].length;
var radius = pieAnimRadii[i]*(1-ratio);
for (var j = 0; j0);
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "translate(-10000, -10000)", pieAnimAngles = this._pieAnimAngles;
for (var i = 0; i= .5) // major arc
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 0 0 ");
}
else
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 0 ");
}
sb.append(x2);
sb.append(",").append(y2);
sb.append("z");
// set the centroids as expandos
if(this._tooltipsVisible)
{
pathElem.setAttribute("_apcGx", Math.round((x1+x2)/3));
pathElem.setAttribute("_apcGy", Math.round((y1+y2)/3));
}
pathElem.setAttribute("d", sb.toString());
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("yValueIndex", iGroup);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
pieStart += valueRatio;
pieElems[i] = pathElem;
}
for (var i = 0; i< nPies; ++i)
{
// calculate the pie gradient:
pieContainer.appendChild(pieElems[i]);
}
}
ApachePieChart.prototype._draw3DPies = function(
pieContainer, quadWidth,
quadHeight, iGroup)
{
var svgDoc = this._svgDoc, model = this._model, yValues = model.getYValues();
var groupLabels = model.getGroupLabels(), seriesColors = model.getSeriesColors();
var pieSize = Math.min(quadWidth/2, quadHeight/2);
var pieTotal = 0;
if(iGroup == -1)
iGroup = 0;
var nPies = yValues[iGroup].length;
for (var i = 0; i < nPies; ++i)
{
pieTotal += yValues[iGroup][i];
}
var perspectiveHeight = pieSize/4, pieElems = new Array(nPies),
ringElems = new Array(nPies), edgeElems = new Array(nPies);
var dataElems = this._dataElems, animate = (this._animDuration>0);
if( perspectiveHeight> ApachePieChart._MAX_PERSPECTIVE_HEIGHT )
perspectiveHeight = ApachePieChart._MAX_PERSPECTIVE_HEIGHT;
var pathElem = svgDoc.getElementById("piePathPrototype"), pieStart = 0;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "translate(-10000, -10000)", pieAnimAngles = this._pieAnimAngles;
for (var i = 0; i < nPies; ++i)
{
pathElem = pathElem.cloneNode(false);
var valueRatio = 1 - (yValues[iGroup][i])/(pieTotal);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
pieAnimAngles.push(pieStart+valueRatio/2);
}
var arcBeginX, arcBeginY, arcEndX, arcEndY;
arcBeginX = pieSize*Math.cos(pieStart*Math.PI*2);
arcBeginY = pieSize*Math.sin(pieStart*Math.PI*2);
var sb = new ApacheChartBuffer();
sb.append("M0,0L").append(arcBeginX).append(",").append(arcBeginY);
arcEndX = pieSize*Math.cos((pieStart+valueRatio)*Math.PI*2);
arcEndY = pieSize*Math.sin((pieStart+valueRatio)*Math.PI*2);
if (valueRatio >= .5)
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 0 0 ");
}
else
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 0 ");
}
sb.append(arcEndX).append(",").append(arcEndY);
sb.append("z");
// set the centroid as expandos
if(this._tooltipsVisible)
{
pathElem.setAttribute("_apcGx", Math.round((arcBeginX+arcEndX)/3));
pathElem.setAttribute("_apcGy", Math.round((arcBeginY+arcEndY)/3));
}
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("yValueIndex", iGroup);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
var pathRingElem = pathElem.cloneNode(false);
var pathEdgeElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathRingElem);
pathRingElem.setAttribute("transform",defaultTransform);
dataElems.push(pathEdgeElem);
pathEdgeElem.setAttribute("transform",defaultTransform);
}
pathElem.setAttribute("d", sb.toString());
sb = new ApacheChartBuffer();
sb.append("M").append(arcBeginX).append(",").append(arcBeginY);
if (valueRatio >= .5) // major arc
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 0 0 ");
}
else
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 0 ");
}
sb.append(arcEndX).append(",").append(arcEndY);
sb.append("v").append(perspectiveHeight);
if (valueRatio >= .5) // major arc
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 0 1 ");
}
else
{
sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 1 ");
}
sb.append(arcBeginX).append(",").append(arcBeginY+perspectiveHeight);
sb.append("z");
pathRingElem.setAttribute("d", sb.toString());
sb = new ApacheChartBuffer();
sb.append("M0,0L");
sb.append(arcBeginX).append(",").append(arcBeginY);
sb.append("v").append(perspectiveHeight);
sb.append("L").append(0).append(",").append(perspectiveHeight);
sb.append("z");
sb.append("M0,0L");
sb.append(arcEndX).append(",").append(arcEndY);
sb.append("v").append(perspectiveHeight);
sb.append("L").append(0).append(",").append(perspectiveHeight);
sb.append("z");
pathEdgeElem.setAttribute("d", sb.toString());
pieStart += valueRatio;
pieElems[i] = pathElem;
ringElems[i] = pathRingElem;
edgeElems[i] = pathEdgeElem;
}
// For the top half, edges have preference over rings
var totalRatio = 0;
for (var i = 0; i< nPies; ++i)
{
if(totalRatio <= .5)
pieContainer.appendChild(ringElems[i]);
totalRatio += (yValues[iGroup][i])/(pieTotal);
}
totalRatio = 0;
for (var i = 0; i< nPies; ++i)
{
if(totalRatio <= .5)
pieContainer.appendChild(edgeElems[i]);
totalRatio += (yValues[iGroup][i])/(pieTotal);
}
// For the bottom half, rings have preference over edges
totalRatio = 0;
for (var i = 0; i< nPies; ++i)
{
if(totalRatio > .5)
pieContainer.appendChild(edgeElems[i]);
totalRatio += (yValues[iGroup][i])/(pieTotal);
}
totalRatio = 0;
for (var i = 0; i< nPies; ++i)
{
if(totalRatio > .5)
pieContainer.appendChild(ringElems[i]);
totalRatio += (yValues[iGroup][i])/(pieTotal);
}
for (var i = 0; i< nPies; ++i)
{
pieContainer.appendChild(pieElems[i]);
}
}
ApachePieChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
var evtTarget = e.target;
var ctm = evtTarget.parentNode.getCTM();
// display the tooltip at the centroid
return {x:(ctm.e + parseInt(evtTarget.getAttribute("_apcGx"))),
y:(ctm.f + ctm.d*parseInt(evtTarget.getAttribute("_apcGy")) - ttBBox.height)};
}
ApachePieChart._MAX_PERSPECTIVE_HEIGHT = 30;
////////////////////////////////////////////////////////////////////
// Area chart subclass
////////////////////////////////////////////////////////////////////
function ApacheAreaChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheAreaChart);
ApacheAreaChart.prototype.Init = function(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
ApacheAreaChart.superclass.Init.call(this, type, model, svgEmbedId,
isPerspective, legendPosition);
this._toolTips = [];
}
ApacheAreaChart.prototype.SetStopOpacity = function(stopNode)
{
// In gecko opacity does not mix with stop-opacity, so use a lower value
stopNode.setAttribute("stop-opacity", ApacheChart._DEFAULT_STOP_OPACITY/2);
}
ApacheAreaChart.prototype.DrawChartData = function()
{
var rootElem = this._rootElement;
if(this._tooltipsVisible)
{
rootElem.addEventListener("mousemove",this.ShowToolTipCallback,false);
rootElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
if(this._isPerspective)
this._drawPerspectiveAreas();
else
this._drawAreas();
}
ApacheAreaChart.prototype.SetDataAnimStep = function(ratio)
{
var model = this._model,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var yValues = model.getYValues(), yValueCount = yValues.length;
var animElems = this._dataElems, animPathCount = (this._isPerspective)? (yValueCount-1):1;
var margins = this._margins, marginBottom = margins.bottom;
var cy = (this._height - marginBottom);
var newRatio = ratio*seriesCount, animSeriesIndex = 0;
if(newRatio > 1)
{
animSeriesIndex = Math.floor(newRatio);
if(animSeriesIndex >= seriesCount)
animSeriesIndex = seriesCount - 1;
newRatio = newRatio - Math.floor(newRatio);
}
// We will make each series appear separately
var i = animSeriesIndex;
for (var j = 0; j < animPathCount; ++j)
{
var ty = (1-newRatio)*cy;
animElems[i*animPathCount+j].setAttribute("transform", "translate(0,"+ty+") scale(1,"+newRatio+")");
if(i>0)
{
animElems[(i-1)*animPathCount+j].setAttribute("transform", "scale(1,1)");
}
}
// make sure that everything is scaled properly at the end
if(ratio == 1)
{
for(var i = 0; i < seriesCount; ++i)
{
for (var j = 0; j < animPathCount; ++j)
{
animElems[i*animPathCount+j].setAttribute("transform", "scale(1,1)");
}
}
}
}
/**
* Overridden to indicate that the group label is edge aligned instead of center aligned
*/
ApacheAreaChart.prototype.IsGroupLabelCentered = function()
{
return false;
}
ApacheAreaChart.prototype.GetVLineCount = function()
{
var xMajorCount = this._xMajorGridCount;
if(xMajorCount >= 0)
return xMajorCount;
else
{
// Area Chart has one vertical line less since the
// first line represents a value
return this._model.getGroupLabels().length-1;
}
}
ApacheAreaChart.prototype._drawAreas = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var pathElem = svgDoc.getElementById("areaPathPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var isStacked = (this._type == ApacheChart.TYPE_AREA_STACKED);
var yValueCount = yValues.length;
var barWidth = (gridWidth/(Math.max(yValueCount,groupCount)-1));
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(1,0.00001)";
var dx, dy, cumYs = [], stackBase;
for (var i = 0; i< seriesCount; ++i)
{
dx = marginLeft;
dy = marginTop + gridHeight;
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
if(i == 0 || !isStacked)
sb.append("M").append(dx).append(",").append(dy);
else if(isStacked)
sb.append("M").append(dx).append(",").append(cumYs[0]);
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
stackBase = (i==0?minValue:0);
for (var j = 0; j < yValueCount; ++j)
{
if(isStacked)
{
if(null == cumYs[j])
cumYs[j] = gridHeight + marginTop;
dy = (cumYs[j] -= gridHeight*(yValues[j][i]-stackBase)/(maxValue-minValue));
}
else
dy = gridHeight + marginTop - gridHeight*(yValues[j][i]- minValue)/(maxValue-minValue);
sb.append("L").append(dx).append(",").append(dy);
if(j != yValueCount - 1)
dx += barWidth;
}
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("seriesIndex", i);
pathElem.addEventListener("click",this.ClickCallback,false);
if(i == 0 || !isStacked)
{
sb.append("L").append(dx).append(",").append(gridHeight + marginTop);
sb.append("Z");
}
else
{
for (var j = yValueCount-1; j>=0; --j)
{
var prevY = cumYs[j]+gridHeight*(yValues[j][i]-stackBase)/(maxValue-minValue);
sb.append("L").append(dx).append(",").append(prevY);
dx -= barWidth;
}
}
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
}
}
ApacheAreaChart.prototype._drawPerspectiveAreas = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var pathElem = svgDoc.getElementById("areaPathPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var isStacked = (this._type == ApacheChart.TYPE_AREA_STACKED);
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var yValueCount = yValues.length;
var barWidth = (gridWidth/(Math.max(yValueCount,groupCount)-1)), stackBase;
var gridBottom = gridHeight + marginTop + yOffset, dx, dy, cumYs = [];
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(1,0.00001)";
for (var i = 0; i< seriesCount; ++i)
{
dx = marginLeft;
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
// If we use non zero min and it is a stacked graph, we need to remove the min for only
// the first series.
stackBase = (i==0?minValue:0);
for (var j = 0; j < yValueCount; ++j)
{
if(isStacked)
{
if(null == cumYs[j])
cumYs[j] = gridBottom;
dy = (cumYs[j] -= gridHeight*(yValues[j][i]-stackBase)/(maxValue-minValue));
}
else
dy = gridBottom - gridHeight*(yValues[j][i]-minValue)/(maxValue-minValue);
if(j != yValueCount - 1)
{
pathElem = pathElem.cloneNode(false);
sb.append("M").append(dx).append(",").append(dy);
sb.append("l").append(xOffset).append(",").append(-yOffset);
var nextdy, nextdx = dx+barWidth;
if(isStacked)
{
if(null == cumYs[j+1])
cumYs[j+1] = gridBottom;
nextdy = (cumYs[j+1] - gridHeight*(yValues[j+1][i]-stackBase)/(maxValue-minValue));
}
else
nextdy = gridBottom - gridHeight*(yValues[j+1][i]-minValue)/(maxValue-minValue)
sb.append("L").append(nextdx+xOffset).append(",").append(nextdy-yOffset);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("L").append(dx).append(",").append(dy);
sb.append("M").append(nextdx).append(",").append(nextdy);
sb.append("l").append(xOffset).append(",").append(-yOffset);
var prevSeriesY, prevSeriesY2;
if(i == 0 || !isStacked)
{
sb.append("L").append(nextdx+xOffset).append(",").append(gridHeight + marginTop);
}
else
{
sb.append("L").append(nextdx+xOffset).append(",").append(cumYs[j+1]-yOffset);
}
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("L").append(nextdx).append(",").append(nextdy);
sb.append("M").append(dx).append(",").append(dy);
sb.append("L").append(nextdx).append(",").append(nextdy);
if(i == 0 || !isStacked)
{
sb.append("L").append(nextdx).append(",").append(gridBottom);
sb.append("L").append(dx).append(",").append(gridBottom);
}
else
{
sb.append("L").append(nextdx).append(",").append(cumYs[j+1]);
sb.append("L").append(dx).append(",").append(
cumYs[j]+gridHeight*(yValues[j][i]-stackBase)/(maxValue-minValue));
}
sb.append("L").append(dx).append(",").append(dy);
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("yValueIndex", j);
pathElem.setAttribute("seriesIndex", i);
pathElem.addEventListener("click",this.ClickCallback,false);
dx += barWidth;
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
}
}
}
}
ApacheAreaChart.prototype.GetChartEvent = function(e, seriesYs)
{
var clientX = e.clientX, clientY = e.clientY, evtTarget = e.target;
var isStacked = (this._type == ApacheChart.TYPE_AREA_STACKED);
var isPerspective = this._isPerspective;
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var model = this._model, yValues = model.getYValues();
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length;
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var yValueCount = yValues.length;
var barWidth = (gridWidth/(Math.max(yValueCount,groupCount)-1));
if(isPerspective)
{
gridWidth -= xOffset;
gridHeight -= yOffset;
}
if(clientX < marginLeft ||
clientX>(marginLeft + gridWidth + (isPerspective?xOffset:0)) ||
clientY < marginTop ||
clientY>(marginTop + gridHeight + (isPerspective?yOffset:0)))
{
return null;
}
var dx, dy, dy1, cumYs = [], seriesIndices = [], seriesValues = [];
var gridBottom = gridHeight + marginTop +(isPerspective?yOffset:0);
var seriesCount = model.getSeriesLabels().length, stackBase, insideStacked = false;
if(!seriesYs)
seriesYs = [];
for (var i = 0; i< seriesCount && !insideStacked; ++i)
{
dx = marginLeft;
stackBase = (i==0?minValue:0);
for (var j = 0; j < yValueCount; ++j)
{
if(isStacked)
{
if(null == cumYs[j])
cumYs[j] = gridBottom;
if(null == cumYs[j+1] && (j != yValueCount -1))
cumYs[j+1] = gridBottom;
cumYs[j] -= gridHeight*(yValues[j][i]-stackBase)/(maxValue-minValue);
}
if(j == yValueCount - 1)
continue;
if(clientX > dx && clientX < (dx+barWidth))
{
if(isStacked)
{
dy1 = cumYs[j];
dy2 = (cumYs[j+1] - gridHeight*(yValues[j+1][i]-stackBase)/(maxValue-minValue));
dy = dy1 - (dy1-dy2)*(clientX - dx)/barWidth;
if(clientY >= dy)
{
value = yValues[j][i] + (yValues[j+1][i]-yValues[j][i])*(clientX-dx)/barWidth;
seriesValues.push(value);
seriesIndices.push(i);
seriesYs.push(dy);
insideStacked = true;
break;
}
}
else
{
dy1 = gridBottom -
gridHeight*(yValues[j][i]-minValue)/(maxValue-minValue);
dy = dy1 - (gridHeight*(yValues[j+1][i]-yValues[j][i])/(maxValue-minValue))*(clientX-dx)/barWidth;
// find all the series that the y point matches
if(dy<=clientY)
{
value = yValues[j][i] + (yValues[j+1][i]-yValues[j][i])*(clientX - dx)/barWidth;
seriesValues.push(value);
seriesIndices.push(i);
seriesYs.push(dy);
}
break;
}
}
dx += barWidth;
}
}
return new ApacheChartEvent(seriesIndices,null, seriesValues,null);
}
ApacheAreaChart.prototype.ShowToolTip = function(e)
{
// Hide any existing tooltips
this.HideToolTip();
var seriesYs = [];
var chartEvent = this.GetChartEvent(e, seriesYs);
if(chartEvent == null || chartEvent.getYValues().length == 0)
{
return;
}
this._displayToolTips(chartEvent.getYValues(), chartEvent.getSeriesIndices(), seriesYs, e);
}
ApacheAreaChart.prototype._displayToolTips = function(
seriesValues, seriesIndices,
seriesYs, e)
{
var svgDoc = this._svgDoc, rootElem = this._rootElement;
var model = this._model, seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length,
seriesColors = model.getSeriesColors();
var tooltipCount = seriesIndices.length, toolTips = this._toolTips;
var clientX = e.clientX;
var dx, dy;
for(var i = 0; i this._width)
{
dx -= ttBBox.width;
circleElem.setAttribute("cx",boundingRectElem.getBBox().width);
}
else
{
circleElem.setAttribute("cx",0);
}
if(dy - ttBBox.height < 0)
{
dy += ttBBox.height;
circleElem.setAttribute("cy",0);
}
else
{
circleElem.setAttribute("cy",boundingRectElem.getBBox().height);
}
boundingRectElem.setAttribute("stroke", seriesColors[seriesIndex]);
circleElem.setAttribute("stroke",seriesColors[seriesIndex]);
toolTip.setAttribute("transform","translate("+dx+","+dy+")");
}
}
ApacheAreaChart.prototype.HideToolTip = function(e)
{
var tooltips = this._toolTips, tooltipCount = tooltips.length;
for(var i = 0; i0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var lineDot = svgDoc.getElementById("lineDotPrototype"),
pathElem = svgDoc.getElementById("linePathPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var yValueCount = yValues.length;
var barWidth = gridWidth/Math.max(yValueCount,groupCount);
var dx, dy;
var gradientsUsed = this._gradientsUsed;
// Adobe plugin does not like 0 for scale and gecko does not like a low number for circles
var defaultTransform = this._isIE?"scale(0.00001,1)":"scale(0,1)";
for (var i = 0; i< seriesCount; ++i)
{
// For combo charts we will draw every alternate(odd) series
if(isCombo && i%2 == 0)
continue;
dx = marginLeft+barWidth/2;
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
for (var j = 0; j < yValueCount; ++j)
{
dy = gridHeight + marginTop - gridHeight*(yValues[j][i]-minValue)/(maxValue-minValue);
if(j == 0)
sb.append("M").append(dx).append(",").append(dy);
else
sb.append("L").append(dx).append(",").append(dy);
lineDot = lineDot.cloneNode(false);
lineDot.setAttribute("cx", dx);
lineDot.setAttribute("cy", dy);
if(gradientsUsed)
lineDot.setAttribute("fill", "url(#gradient"+i+")");
else
lineDot.setAttribute("fill", seriesColors[i]);
lineDot.setAttribute("stroke", seriesColors[i]);
if(animate)
{
lineDot.setAttribute("transform",defaultTransform);
dataElems.push(lineDot);
}
rootElem.appendChild(lineDot);
// There is no fill for lines
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
dx += barWidth;
}
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
}
}
ApacheLineChart.prototype.__drawPerspectiveLines = function(isCombo)
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var pathElem = svgDoc.getElementById("linePath3dPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var yValueCount = yValues.length;
var barWidth = (gridWidth/Math.max(yValueCount,groupCount));
var gridBottom = gridHeight + marginTop + yOffset, dx, dy;
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(0.00001,1)";
for (var i = 0; i< seriesCount; ++i)
{
// For combo charts we will draw every alternate(odd) series
if(isCombo && i%2 == 0)
continue;
dx = marginLeft+barWidth/2;
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
for (var j = 0; j < yValueCount; ++j)
{
dy = gridBottom - gridHeight*(yValues[j][i]-minValue)/(maxValue-minValue);
if(j != yValueCount - 1)
{
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
sb.append("M").append(dx).append(",").append(dy);
sb.append("l").append(xOffset).append(",").append(-yOffset);
var nextdy = gridBottom - gridHeight*(yValues[j+1][i]-minValue)/(maxValue-minValue);
var nextdx = dx+barWidth;
sb.append("L").append(nextdx+xOffset).append(",").append(nextdy-yOffset);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("L").append(dx).append(",").append(dy);
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mousemove",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
dx += barWidth;
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
}
}
}
}
ApacheLineChart.prototype.ShowToolTip = function(e)
{
// first hide any existing tooltip
this.HideToolTip();
ApacheLineChart.superclass.ShowToolTip.call(this, e);
}
ApacheLineChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
return {x:(e.clientX+20), y:(e.clientY+20)};
}
ApacheLineChart.prototype.GetChartEvent = function(e)
{
var evtTarget = e.target;
var i = parseInt(evtTarget.getAttribute("seriesIndex"));
var clientX = e.clientX;
var isPerspective = this._isPerspective;
var yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var model = this._model, yValues = model.getYValues();
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length;
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var yValueCount = yValues.length;
var barWidth = (gridWidth/Math.max(yValueCount,groupCount));
var gridBottom = gridHeight + marginTop +(isPerspective?yOffset:0);
var dx = marginLeft+barWidth/2, value = 0.0;
for (var j = 0; j < yValueCount; ++j)
{
if(j == yValueCount - 1)
continue;
if(clientX > dx && clientX < (dx+barWidth))
{
value = yValues[j][i] + (yValues[j+1][i]-yValues[j][i])*(clientX - dx)/barWidth;
break;
}
dx += barWidth;
}
return new ApacheChartEvent([i],null, [value],null);
}
ApacheLineChart.prototype.FillToolTipData = function(boundingRectElem, circleElem, e)
{
var chartEvent = this.GetChartEvent(e);
var i = chartEvent.getSeriesIndices()[0], value = chartEvent.getYValues()[0];
var seriesLabels = this._model.getSeriesLabels();
var textElem = boundingRectElem.nextSibling.nextSibling;
textElem.firstChild.data = seriesLabels[i]+
": "+this._formatValue(value);
var labelWidth = textElem.getBBox().width;
//We do not need the next label
textElem = textElem.nextSibling.nextSibling;
textElem.firstChild.data = "";
var rectWidth = labelWidth+2*ApacheChart._TEXT_MARGIN;
boundingRectElem.setAttribute("width",rectWidth);
boundingRectElem.setAttribute("stroke", seriesColors[i]);
circleElem.setAttribute("r",0);
}
////////////////////////////////////////////////////////////////////
// ScatterPlot chart subclass
////////////////////////////////////////////////////////////////////
function ApacheScatterPlotChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheScatterPlotChart);
ApacheScatterPlotChart.prototype.Init = function(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
ApacheScatterPlotChart.superclass.Init.call(this, type, model, svgEmbedId,
isPerspective, legendPosition);
//this._cxs = undefined;
//this._cys = undefined;
}
ApacheScatterPlotChart.prototype.DrawChartData = function()
{
if(this._isPerspective)
this._drawPerspectivePoints();
else
this._drawPoints();
}
ApacheScatterPlotChart.prototype.SetDataAnimStep = function(ratio)
{
var isPerspective = this._isPerspective;
var cxs = this._cxs, cys = this._cys, gridCx, gridCy;
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var animElems = this._dataElems, animCount = cxs.length, elemIndex = 0;
if(isPerspective)
marginLeft += ApacheChart._XOFFSET_PERSPECTIVE;
gridCx = gridWidth/2 + marginLeft;
gridCy = gridHeight/2 + marginTop;
// we are going to animate by starting the dot at the middle and work towards its destination
for(var i = 0; i < animCount; ++i)
{
var cx = gridCx - (gridCx - cxs[i])*ratio;
var cy = gridCy - (gridCy - cys[i])*ratio;
var elem = animElems[elemIndex++];
elem.setAttribute("cx", cx);
elem.setAttribute("cy", cy);
if(isPerspective)
{
elem = animElems[elemIndex++];
elem.setAttribute("cx", cx);
elem.setAttribute("cy", cy);
}
}
}
ApacheScatterPlotChart.prototype.SetGridAnimStep = function(ratio)
{
var animElems = this._gridElems, animCount = animElems.length;
for(var i = 0; i < animCount; ++i)
{
animElems[i].setAttribute("fill-opacity", ratio);
animElems[i].setAttribute("transform", "scale(1,1)");
}
}
/**
* Overridden to indicate that the group label is edge aligned instead of center aligned
*/
ApacheScatterPlotChart.prototype.IsGroupLabelCentered = function()
{
return false;
}
ApacheScatterPlotChart.prototype.GetVLineCount = function()
{
var xMajorCount = this._xMajorGridCount;
if(xMajorCount >= 0)
return xMajorCount;
else
{
// Area Chart has one vertical line less since the
// first line represents a value
return this._model.getGroupLabels().length-1;
}
}
ApacheScatterPlotChart.prototype._drawPoints = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var dotElem = svgDoc.getElementById("scatterDotPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), xValues = model.getXValues(),
yValues = model.getYValues(), nValues = yValues.length;
var defaultTransform = "translate(0,100000)";
var minYValue = model.getMinYValue(), maxYValue = model.getMaxYValue();
var minXValue = model.getMinXValue(), maxXValue = model.getMaxXValue();
var barWidth = (gridWidth/(groupCount-1));
var cxs, cys, dx, dy, gridCx, gridCY;
var gradientsUsed = this._gradientsUsed;
if(animate)
{
cxs = this._cxs = [];
cys = this._cys = [];
gridCx = gridWidth/2 + marginLeft;
gridCy = gridHeight/2 + marginTop;
}
for (var i = 0; i< seriesCount; ++i)
{
for (var j = 0; j < nValues; ++j)
{
dy = gridHeight + marginTop - gridHeight*(yValues[j][i]-minYValue)/(maxYValue-minYValue);
dx = marginLeft + gridWidth*(xValues[j][i]-minXValue)/(maxXValue-minXValue);
dotElem = dotElem.cloneNode(false);
if(gradientsUsed)
dotElem.setAttribute("fill", "url(#gradient"+i+")");
else
dotElem.setAttribute("fill", seriesColors[i]);
dotElem.setAttribute("stroke", seriesColors[i]);
dotElem.setAttribute("yValueIndex", j);
dotElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
dotElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
dotElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
dotElem.addEventListener("click",this.ClickCallback,false);
if(animate)
{
dataElems.push(dotElem);
dotElem.setAttribute("cx", gridCx);
dotElem.setAttribute("cy", gridCy);
// we will use it during animation
cxs.push(dx);
cys.push(dy);
}
else
{
dotElem.setAttribute("cx", dx);
dotElem.setAttribute("cy", dy);
}
rootElem.appendChild(dotElem);
}
}
}
ApacheScatterPlotChart.prototype._drawPerspectivePoints = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var dotElem = svgDoc.getElementById("scatter3dDotPrototype");
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), xValues = model.getXValues(),
yValues = model.getYValues(), nValues = yValues.length;
var minYValue = model.getMinYValue(), maxYValue = model.getMaxYValue();
var minXValue = model.getMinXValue(), maxXValue = model.getMaxXValue();
var barWidth = (gridWidth/(groupCount-1));
var gridBottom = gridHeight + marginTop + yOffset, cxs, cys, dx, dy, gridCx, gridCY;
var gradientsUsed = this._gradientsUsed;
if(animate)
{
cxs = this._cxs = [];
cys = this._cys = [];
gridCx = gridWidth/2 + marginLeft +xOffset;
gridCy = gridHeight/2 + marginTop;
}
for (var i = 0; i< seriesCount; ++i)
{
for (var j = 0; j < nValues; ++j)
{
dy = gridBottom - gridHeight*(yValues[j][i]-minYValue)/(maxYValue-minYValue);
dx = marginLeft + gridWidth*(xValues[j][i]-minXValue)/(maxXValue-minXValue);
dotElem = dotElem.cloneNode(false);
if(animate)
{
dataElems.push(dotElem);
dotElem.setAttribute("cx", gridCx);
dotElem.setAttribute("cy", gridCy);
// we will use it during animation
cxs.push(dx);
cys.push(dy);
}
else
{
dotElem.setAttribute("cx", dx);
dotElem.setAttribute("cy", dy);
}
if(gradientsUsed)
dotElem.setAttribute("fill", "url(#gradient"+i+")");
else
dotElem.setAttribute("fill", seriesColors[i]);
dotElem.setAttribute("stroke", seriesColors[i]);
dotElem.setAttribute("yValueIndex", j);
dotElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
dotElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
dotElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
dotElem.addEventListener("click",this.ClickCallback,false);
var shadowElem = dotElem.cloneNode(false);
if(animate)
{
dataElems.push(shadowElem);
}
shadowElem.setAttribute("fill","#333333");
shadowElem.setAttribute("opacity","0.5");
shadowElem.setAttribute("stroke","none");
shadowElem.setAttribute("transform", "translate(3,3)");
rootElem.appendChild(shadowElem);
rootElem.appendChild(dotElem);
}
}
}
ApacheScatterPlotChart.prototype.GetChartEvent = function(e)
{
var evtTarget = e.target;
var i = parseInt(evtTarget.getAttribute("seriesIndex")),
j = parseInt(evtTarget.getAttribute("yValueIndex"));
var model = this._model, xValues = model.getXValues(),
yValues = model.getYValues();
return new ApacheChartEvent([i],[j], [yValues[j][i]],[xValues[j][i]]);
}
ApacheScatterPlotChart.prototype.FillToolTipData = function(boundingRectElem, circleElem, e)
{
var chartEvent = this.GetChartEvent(e);
var i = chartEvent.getSeriesIndices()[0],
yValue = chartEvent.getYValues()[0]
xValue = chartEvent.getXValues()[0];
var model = this._model, seriesLabels = model.getSeriesLabels();
var textElem = boundingRectElem.nextSibling.nextSibling;
textElem.firstChild.data = seriesLabels[i]+
": ("+ this._formatValue(xValue) +
") (" + this._formatValue(yValue) +")";
var labelWidth = textElem.getBBox().width;
//We do not need the next label
textElem = textElem.nextSibling.nextSibling;
textElem.firstChild.data = "";
var rectWidth = labelWidth+2*ApacheChart._TEXT_MARGIN;
boundingRectElem.setAttribute("width",rectWidth);
boundingRectElem.setAttribute("stroke", seriesColors[i]);
circleElem.setAttribute("r",0);
}
////////////////////////////////////////////////////////////////////
// XYLine Chart subclass
// Note: chart x values should be in the ascending order other wise
// the chart does not make sense
////////////////////////////////////////////////////////////////////
function ApacheXYLineChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheScatterPlotChart, ApacheXYLineChart);
ApacheXYLineChart.prototype.SetDataAnimStep = function(ratio)
{
// bypass the parent and go directly into Chart
ApacheChart.prototype.SetDataAnimStep.call(this, ratio);
}
ApacheXYLineChart.prototype.DrawChartData = function()
{
if(this._isPerspective)
this._drawPerspectiveXYValues();
else
this._drawXYValues();
}
ApacheXYLineChart.prototype.AnimAlongXAxis = function()
{
// always around x axis
return true;
}
ApacheXYLineChart.prototype._drawXYValues = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var dotElem, pathElem;
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), xValues = model.getXValues(),
yValues = model.getYValues(), nValues = yValues.length;
var defaultTransform = "scale(0.00001,1)";
var minYValue = model.getMinYValue(), maxYValue = model.getMaxYValue();
var minXValue = model.getMinXValue(), maxXValue = model.getMaxXValue();
var barWidth = (gridWidth/(groupCount-1));
var dx, dy;
pathElem = svgDoc.getElementById("linePathPrototype");
for (var i = 0; i< seriesCount; ++i)
{
var sb = new ApacheChartBuffer();
dx = marginLeft;
dy = gridHeight + marginTop;
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
for (var j = 0; j < nValues; ++j)
{
dy = gridHeight + marginTop - gridHeight*(yValues[j][i]-minYValue)/(maxYValue-minYValue);
dx = marginLeft + gridWidth*(xValues[j][i]-minXValue)/(maxXValue-minXValue);
if(j==0)
sb.append("M").append(dx).append(",").append(dy);
else
sb.append("L").append(dx).append(",").append(dy);
}
pathElem.setAttribute("seriesIndex", i);
pathElem.setAttribute("stroke", seriesColors[i]);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mousemove",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
}
}
ApacheXYLineChart.prototype._drawPerspectiveXYValues = function()
{
var svgDoc = this._svgDoc, model = this._model, margins = this._margins;
var rootElem = this._rootElement, dataElems = this._dataElems, animate = (this._animDuration>0);
var xOffset = ApacheChart._XOFFSET_PERSPECTIVE, yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right - xOffset);
var gridHeight = (this._height - marginTop - margins.bottom - yOffset);
var dotElem, pathElem;
var groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), xValues = model.getXValues(),
yValues = model.getYValues(), nValues = yValues.length;
var defaultTransform = "scale(0.00001,1)";
var minYValue = model.getMinYValue(), maxYValue = model.getMaxYValue();
var minXValue = model.getMinXValue(), maxXValue = model.getMaxXValue();
var gridBottom = gridHeight + marginTop+yOffset, dx, dy;
var gradientsUsed = this._gradientsUsed;
pathElem = svgDoc.getElementById("linePath3dPrototype");
for (var i = 0; i< seriesCount; ++i)
{
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
for (var j = 0; j < nValues; ++j)
{
dy = gridBottom - gridHeight*(yValues[j][i]-minYValue)/(maxYValue-minYValue);
dx = marginLeft + gridWidth*(xValues[j][i]-minXValue)/(maxXValue-minXValue);
if(j != nValues - 1)
{
sb.append("M").append(dx).append(",").append(dy);
sb.append("l").append(xOffset).append(",").append(-yOffset);
var nextdy, nextdx;
nextdx = marginLeft + gridWidth*(xValues[j+1][i]-minXValue)/(maxXValue-minXValue);
nextdy = gridBottom -
gridHeight*(yValues[j+1][i]-minYValue)/(maxYValue-minYValue);
sb.append("L").append(nextdx+xOffset).append(",").append(nextdy-yOffset);
sb.append("l").append(-xOffset).append(",").append(yOffset);
sb.append("L").append(dx).append(",").append(dy);
}
}
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mousemove",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
}
}
ApacheXYLineChart.prototype.ShowToolTip = function(e)
{
// first hide any existing tooltip
this.HideToolTip();
ApacheXYLineChart.superclass.ShowToolTip.call(this, e);
}
ApacheXYLineChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
return {x:(e.clientX+20), y:(e.clientY+20)};
}
ApacheXYLineChart.prototype.GetChartEvent = function(e)
{
var evtTarget = e.target;
var i = parseInt(evtTarget.getAttribute("seriesIndex"));
var clientX = e.clientX, clientY = e.clientY, evtTarget = e.target;
var isPerspective = this._isPerspective;
var yOffset = ApacheChart._YOFFSET_PERSPECTIVE;
var model = this._model, xValues = model.getXValues(),
yValues = model.getYValues(), nValues = yValues.length;
var minYValue = model.getMinYValue(), maxYValue = model.getMaxYValue();
var minXValue = model.getMinXValue(), maxXValue = model.getMaxXValue();
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var gridBottom = gridHeight + marginTop +(isPerspective?yOffset:0);
var dx, dy, xValue = 0.0, yValue = 0.0;
var nextdy, nextdx;
for (var j = 0; j < nValues; ++j)
{
if(j != nValues - 1)
{
dx = marginLeft + gridWidth*(xValues[j][i]-minXValue)/(maxXValue-minXValue);
nextdx = marginLeft + gridWidth*(xValues[j+1][i]-minXValue)/(maxXValue-minXValue);
if(clientX > dx && clientX < (dx+nextdx))
{
dy = gridBottom - gridHeight*(yValues[j][i]-minYValue)/(maxYValue-minYValue);
nextdy = gridBottom -
gridHeight*(yValues[j+1][i]-minYValue)/(maxYValue-minYValue);
yValue = yValues[j][i] + (yValues[j+1][i]-yValues[j][i])*(clientY - dy)/(nextdy-dy);
xValue = xValues[j][i] + (xValues[j+1][i]-xValues[j][i])*(clientX - dx)/(nextdx-dx);
break;
}
}
}
return new ApacheChartEvent([i],null, [yValue],[xValue]);
}
ApacheXYLineChart.prototype.FillToolTipData = function(boundingRectElem, circleElem, e)
{
var chartEvent = this.GetChartEvent(e);
var i = chartEvent.getSeriesIndices()[0],
yValue = chartEvent.getYValues()[0]
xValue = chartEvent.getXValues()[0];
var model = this._model, seriesLabels = model.getSeriesLabels();
var textElem = boundingRectElem.nextSibling.nextSibling;
textElem.firstChild.data = seriesLabels[i]+
": ("+ this._formatValue(xValue) +
") (" + this._formatValue(yValue) +")";
var labelWidth = textElem.getBBox().width;
//We do not need the next label
textElem = textElem.nextSibling.nextSibling;
textElem.firstChild.data = "";
var rectWidth = labelWidth+2*ApacheChart._TEXT_MARGIN;
boundingRectElem.setAttribute("width",rectWidth);
boundingRectElem.setAttribute("stroke", seriesColors[i]);
circleElem.setAttribute("r",0);
}
////////////////////////////////////////////////////////////////////
// Radar chart subclass
////////////////////////////////////////////////////////////////////
function ApacheRadarChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheRadarChart);
ApacheRadarChart.prototype.Init = function(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
ApacheRadarChart.superclass.Init.call(this, type, model, svgEmbedId,
isPerspective, legendPosition);
this._toolTips = [];
}
ApacheRadarChart.prototype.draw = function()
{
this._yLabels = [];
ApacheRadarChart.superclass.draw.call(this);
delete this._yLabels;
}
ApacheRadarChart.prototype.SetGridAnimStep = function(ratio)
{
var animElems = this._gridElems, animCount = animElems.length;
for(var i = 0; i < animCount; ++i)
{
animElems[i].setAttribute("fill-opacity",ratio);
}
}
ApacheRadarChart.prototype.SetDataAnimStep = function(ratio)
{
var animElems = this._dataElems, animCount = animElems.length;
var margins = this._margins, marginLeft = margins.left, marginTop = margins.top;
var isRadarArea =(this._type == ApacheChart.TYPE_RADAR_AREA);
var model = this._model, groupLabels = model.getGroupLabels(), groupCount = groupLabels.length,
seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var cx = marginLeft+gridWidth/2, cy = marginTop+gridHeight/2;
var newRatio = ratio*seriesCount, animSeriesIndex = 0;
if(newRatio > 1)
{
animSeriesIndex = Math.floor(newRatio);
if(animSeriesIndex >= seriesCount)
animSeriesIndex = seriesCount - 1;
newRatio = newRatio - Math.floor(newRatio);
}
var tx = (1-newRatio)*cx, ty = (1-newRatio)*cy;
var transform = "translate("+tx+","+ ty+") scale("+newRatio+","+newRatio+")";
// We will make each series appear separately
var i = animSeriesIndex;
this._setRadarSeriesAnimStep(i, animElems, isRadarArea, transform);
if(i>0)
{
this._setRadarSeriesAnimStep(i-1, animElems, isRadarArea, "scale(1,1)");
}
// make sure that everything is scaled properly at the end
if(ratio == 1)
{
for(var i = 0; i < seriesCount; ++i)
{
this._setRadarSeriesAnimStep(i, animElems, isRadarArea, "scale(1,1)");
}
}
}
ApacheRadarChart.prototype._setRadarSeriesAnimStep = function(i, animElems, isRadarArea, transform)
{
animElems[i].setAttribute("transform", transform);
if(!isRadarArea)
{
var dots = animElems["dots"+i];
for(var j = dots.length-1; j>=0; --j)
{
dots[j].setAttribute("transform", transform);
}
}
}
ApacheRadarChart.prototype.DrawChartData = function()
{
this._drawRadar();
// Move the y-value labels to the top since currently there is not concept of z-index in svg
var yLabels = this._yLabels, rootElem = this._rootElement;
for(var i = yLabels.length-1; i >=0; i--)
{
var label = yLabels[i];
if(label)
{
rootElem.removeChild(label);
rootElem.appendChild(label);
}
}
}
ApacheRadarChart.prototype.SetStopOpacity = function(stopNode)
{
// In gecko opacity does not mix with stop-opacity, so use a lower value
stopNode.setAttribute("stop-opacity", ApacheChart._DEFAULT_STOP_OPACITY/2);
}
/**
* Adjusts the legend location when it is at the top
* @param ty(int) the original y location of the legend
*/
ApacheRadarChart.prototype.SetLegendTopAdjustment = function(ty)
{
var container = this._hLabelContainer;
ty -= container.getBBox().height+ApacheChart._TEXT_MARGIN;
return ty;
}
/**
* Adjusts the legend location when it is at the bottom
* @param ty(int) the original y location of the legend
*/
ApacheRadarChart.prototype.SetLegendBottomAdjustment = function(ty)
{
var container = this._hLabelContainer;
if(container.childNodes.length > 0)
{
ty += container.getBBox().height+ApacheChart._TEXT_MARGIN;
}
return ty;
}
/**
* Adjusts the legend location when it is at the Left
* @param tx(int) the original x location of the legend
*/
ApacheRadarChart.prototype.SetLegendLeftAdjustment = function(tx)
{
var container = this._hLabelContainer;
if(container.childNodes.length > 0)
{
tx -= container.getBBox().width+ApacheChart._TEXT_MARGIN;
}
return tx;
}
/**
* Adjusts the legend location when it is at the Right
* @param tx(int) the original x location of the legend
*/
ApacheRadarChart.prototype.SetLegendRightAdjustment = function(tx)
{
var container = this._hLabelContainer;
if(container.childNodes.length > 0)
tx += container.getBBox().width+ApacheChart._TEXT_MARGIN;
return tx;
}
ApacheRadarChart.prototype.DrawGroupLabels = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement, model = this._model;
var container = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
this._hLabelContainer = container;
var labelElems = this._labelElems, animate = (this._animDuration>0);
var groupLabels = model.getGroupLabels(), vLineCount = groupLabels.length;
var labelElem = svgDoc.getElementById("groupLabelPrototype");
var labelText, gLabelElems = this._groupLabelElems;
for(var i = 0; i 0)
{
var bBox = container.getBBox();
var dxVertical = bBox.width+ApacheChart._TEXT_MARGIN,
dyVertical = bBox.height+ApacheChart._TEXT_MARGIN;
this._margins.top += dyVertical;
this._margins.bottom += dyVertical;
this._margins.left += dxVertical;
this._margins.right += dxVertical;
}
}
ApacheRadarChart.prototype.LayoutGroupLabels = function()
{
var model = this._model, margins = this._margins;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight, cy;
var cx = marginLeft+gridWidth/2, radius;
var groupLabels = model.getGroupLabels(), vLineCount = groupLabels.length;
var labelElem, groupWidth = gridWidth/vLineCount;
var container = this._hLabelContainer, childNodes = container.childNodes;
var firstLabel = false, gLabelElems = this._groupLabelElems;
if(childNodes.length == 0)
return;
for(var i = 0; i Math.PI)
dx -= textWidth;
labelElem.setAttribute("x", dx);
}
}
ApacheRadarChart.prototype.DrawGrid = function()
{
// No perspective support for radar
this.Draw2DGrid();
}
ApacheRadarChart.prototype.Draw2DGrid = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement,
model = this._model, margins = this._margins;
var gridElems = this._gridElems, animate = (this._animDuration>0);
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var cx = marginLeft+gridWidth/2, cy = marginTop+gridHeight/2;
var radius = Math.min(gridWidth, gridHeight)/2;
var gradientsUsed = this._gradientsUsed;
var circle = svgDoc.getElementById("radarCirclePrototype").cloneNode(false);
circle.setAttribute("cx", cx);
circle.setAttribute("cy", cy);
circle.setAttribute("r", radius);
if(gradientsUsed)
circle.setAttribute("fill", "url(#gridGradient)");
if(animate)
{
gridElems.push(circle);
circle.setAttribute("fill-opacity","0");
}
rootElem.appendChild(circle);
var vLineCount = this.GetVLineCount(), circleCount = this.GetHLineCount();
// inner circles
circle = svgDoc.getElementById("radarInnerCirclePrototype");
circle.setAttribute("cx", cx);
circle.setAttribute("cy", cy);
for (var i = 0; i< circleCount-1; ++i)
{
circle = circle.cloneNode(false);
if(animate)
{
gridElems.push(circle);
circle.setAttribute("fill-opacity","0");
}
var newRadius = radius - (i+1)*radius/circleCount;
circle.setAttribute("r", newRadius);
rootElem.appendChild(circle);
}
var sb = new ApacheChartBuffer();
var pathElem = svgDoc.getElementById("radarGridPathPrototype").cloneNode(false);
sb.append("M").append(cx).append(",").append(cy);
sb.append("l").append(0).append(",").append(-radius);
for(var i = 0; i0);
var cx = marginLeft+gridWidth/2, cy = marginTop+gridHeight/2;
var radius = Math.min(gridWidth, gridHeight)/2;
var vLineCount = this.GetVLineCount(), circleCount = this.GetHLineCount();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var yLabels = this._yLabels;
var labelElem = svgDoc.getElementById("yLabelPrototype");
labelElem = labelElem.cloneNode(true);
var textHeight = this._addRadarYLabelAt(rootElem, labelElem, circleCount-1,
cx,marginTop, textHeight, this._formatValue(maxValue));
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
labelElem = labelElem.cloneNode(true);
this._addRadarYLabelAt(rootElem, labelElem, circleCount,
cx, cy, textHeight, this._formatValue(minValue));
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
// horizontal lines
for (var i = 0; i< circleCount-1; ++i)
{
var newRadius = (i+1)*radius/circleCount;
var value = ((maxValue-minValue)*(i+1)/circleCount) + minValue;
labelElem = labelElem.cloneNode(true);
this._addRadarYLabelAt(rootElem, labelElem, i, cx,
radius-newRadius+marginTop, textHeight, this._formatValue(value));
if(animate)
{
labelElems.push(labelElem);
labelElem.setAttribute("fill-opacity","0");
}
}
}
ApacheRadarChart.prototype._addRadarYLabelAt = function(
rootElem, labelElem, index,
x, y, textHeight, value)
{
this._yLabels[index] = labelElem;
labelElem.firstChild.data = value;
rootElem.appendChild(labelElem);
if(textHeight == null)
{
var rect = labelElem.getBBox();
textHeight = rect.height;
}
// readjust to right align
var labelMargin = ApacheChart._TEXT_MARGIN,
textLength = labelElem.getBBox().width;
dx = x-textLength-labelMargin;
labelElem.setAttribute("x", dx);
labelElem.setAttribute("y", y+textHeight/2);
return textHeight;
}
ApacheRadarChart.prototype._drawRadar = function()
{
var svgDoc = this._svgDoc, rootElem = this._rootElement,
model = this._model, margins = this._margins;
var dataElems = this._dataElems, animate = (this._animDuration>0), dotElems;
var marginLeft = margins.left, marginTop = margins.top;
var gridWidth = (this._width - marginLeft - margins.right);
var gridHeight = (this._height - marginTop - margins.bottom);
var cx = marginLeft+gridWidth/2, cy = marginTop+gridHeight/2;
var radius = Math.min(gridWidth, gridHeight)/2;
var isRadarArea =(this._type == ApacheChart.TYPE_RADAR_AREA);
var protoName = isRadarArea?"areaPathPrototype":"linePathPrototype";
var lineDot,pathElem = svgDoc.getElementById(protoName);
var seriesLabels = model.getSeriesLabels(), seriesCount = seriesLabels.length;
var seriesColors = model.getSeriesColors(), yValues = model.getYValues();
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var defaultTransform = "scale(0.00001,0.00001)";
var gradientsUsed = this._gradientsUsed;
var yValueCount = yValues.length;
var dx, dy;
if(!isRadarArea)
lineDot = svgDoc.getElementById("lineDotPrototype");
for (var i = 0; i< seriesCount; ++i)
{
var sb = new ApacheChartBuffer();
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
dotElems = dataElems["dots"+i] = [];
}
for (var j = 0; j < yValueCount; ++j)
{
var yPoint = radius*(yValues[j][i]-minValue)/(maxValue-minValue);
var dx= cx + yPoint*Math.sin((j)*2*Math.PI/yValueCount),
dy = cy - yPoint*Math.cos((j)*2*Math.PI/yValueCount);
if(j == 0)
{
sb.append("M").append(dx).append(",").append(dy);
}
else
{
sb.append("L").append(dx).append(",").append(dy);
}
if(!isRadarArea)
{
lineDot = lineDot.cloneNode(false);
lineDot.setAttribute("cx", dx);
lineDot.setAttribute("cy", dy);
if(gradientsUsed)
lineDot.setAttribute("fill", "url(#gradient"+i+")");
else
lineDot.setAttribute("fill", seriesColors[i]);
lineDot.setAttribute("stroke", seriesColors[i]);
if(animate)
{
dotElems.push(lineDot);
lineDot.setAttribute("transform",defaultTransform);
}
rootElem.appendChild(lineDot);
}
}
sb.append("Z");
if(isRadarArea)
{
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
}
else
{
pathElem.setAttribute("fill", "none");
}
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mousemove",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
pathElem.setAttribute("d", sb.toString());
rootElem.appendChild(pathElem);
}
}
ApacheRadarChart.prototype.isPointInPolygon = function(xs, ys, x, y)
{
var i, j, npol = ys.length, inside = false;
for (i = 0, j = npol-1; i < npol; j = i++) {
if ((((ys[i]<=y) && (y(marginLeft + gridWidth) ||
clientY < marginTop ||
clientY>(marginTop + gridHeight))
{
return null;
}
if(!isRadarArea)
seriesIndex = parseInt(evtTarget.getAttribute("seriesIndex"));
if(!seriesXs)
seriesXs = [];
if(!seriesYs)
seriesYs = [];
var dx1, dy1, dx2, dy2, seriesIndices = [], seriesValues = [];
for (var i = 0; i< seriesCount; ++i)
{
if(!isRadarArea && (seriesIndex != i))
continue;
for (var j = 0; j < yValueCount; ++j)
{
var nextYVal = (j != yValueCount -1)? yValues[j+1][i]: yValues[0][i];
var yPoint = radius*(yValues[j][i]-minValue)/(maxValue-minValue);
var yPoint2 = radius*(nextYVal-minValue)/(maxValue-minValue);
var angle = j*2*Math.PI/yValueCount,
nextAngle = (j+1)*2*Math.PI/yValueCount;
var dx1 = cx + yPoint*Math.sin(angle),
dy1 = cy - yPoint*Math.cos(angle);
dx2 = cx + yPoint2*Math.sin(nextAngle);
dy2 = cy - yPoint2*Math.cos(nextAngle);
if(this.isPointInPolygon([cx, dx1, dx2], [cy, dy1, dy2], clientX, clientY))
{
// find the point on the radar that matches the current mouse
// using the angle of the current mouse point
var mousePtAngle = Math.atan2(cy - clientY, clientX - cx);
if(mousePtAngle <= Math.PI/2)
mousePtAngle = Math.PI/2 - mousePtAngle;
else
mousePtAngle = 3*Math.PI/2 + (Math.PI - mousePtAngle);
var ratio = (mousePtAngle - angle)/(nextAngle - angle);
value = yValues[j][i] + (nextYVal-yValues[j][i])*ratio;
seriesValues.push(value);
seriesIndices.push(i);
seriesYs.push(dy1+(dy2-dy1)*ratio);
seriesXs.push(dx1+(dx2-dx1)*ratio);
break;
}
}
}
return new ApacheChartEvent(seriesIndices,null, seriesValues,null);
}
ApacheRadarChart.prototype.ShowToolTip = function(e)
{
// Hide any existing tooltips
this.HideToolTip();
var seriesXs = [], seriesYs = [];
var chartEvent = this.GetChartEvent(e, seriesXs, seriesYs);
if(chartEvent == null || chartEvent.getYValues().length == 0)
{
return;
}
this._displayToolTips(chartEvent.getYValues(), chartEvent.getSeriesIndices(),
seriesYs, seriesXs, e);
}
ApacheRadarChart.prototype._displayToolTips = function(
seriesValues, seriesIndices,
seriesYs, seriesXs, e)
{
var svgDoc = this._svgDoc, rootElem = this._rootElement;
var model = this._model, seriesLabels = model.getSeriesLabels(),
seriesCount = seriesLabels.length, seriesColors = model.getSeriesColors();
var tooltipCount = seriesIndices.length, toolTips = this._toolTips;
var dx, dy;
for(var i = 0; i this._width)
{
dx -= ttBBox.width;
circleElem.setAttribute("cx", boundingRectElem.getBBox().width);
}
else
{
circleElem.setAttribute("cx",0);
}
if(dy - ttBBox.height < 0)
{
dy += ttBBox.height;
circleElem.setAttribute("cy",0);
}
else
{
circleElem.setAttribute("cy",boundingRectElem.getBBox().height);
}
boundingRectElem.setAttribute("stroke", seriesColors[seriesIndex]);
circleElem.setAttribute("stroke",seriesColors[seriesIndex]);
toolTip.setAttribute("transform","translate("+dx+","+dy+")");
}
}
ApacheRadarChart.prototype.HideToolTip = function(e)
{
var tooltips = this._toolTips, tooltipCount = tooltips.length;
for(var i = 0; i= yValueCount)
break;
var groupLabel = (iGroup == -1)?null:groupLabels[iGroup];
var fnlContainer = rootElem.cloneNode(false);
rootElem.appendChild(fnlContainer);
var newHeight = this.DrawGroupLabelTitle(groupLabel, rootElem,
labelElem.cloneNode(true), dx, dy,
quadWidth, quadHeight);
var newWidth = quadWidth - 2*ApacheChart._TEXT_MARGIN;
if(this._isPerspective)
{
// Top ring has a height of 1/6 that of the width. So we need to compensate for half of it.
newHeight -= newWidth/6;
this._drawPerspectiveFunnel(fnlContainer, newWidth, newHeight, iGroup);
fnlContainer.setAttribute("transform",
"translate("+(dx+ApacheChart._TEXT_MARGIN)+","+(dy+newWidth/12)+")");
}
else
{
this._drawFunnel(fnlContainer, newWidth, newHeight, iGroup);
fnlContainer.setAttribute("transform",
"translate("+(dx+ApacheChart._TEXT_MARGIN)+","+dy+")");
}
dx +=quadWidth;
}
dx=margins.left;
dy +=quadHeight+vGap;
}
}
ApacheFunnelChart.prototype.ComputeMinMaxValues = function()
{
}
ApacheFunnelChart.prototype.DrawGroupLabels = function()
{
}
ApacheFunnelChart.prototype.LayoutGroupLabels = function()
{
}
ApacheFunnelChart.prototype.DrawGrid = function()
{
}
ApacheFunnelChart.prototype.DrawYValueLabels = function()
{
}
ApacheFunnelChart.prototype.LayoutYValueLabels = function()
{
}
ApacheFunnelChart.prototype._drawFunnel = function(
fnlContainer, quadWidth,
quadHeight, iGroup)
{
var svgDoc = this._svgDoc, model = this._model, yValues = model.getYValues();
var groupLabels = model.getGroupLabels(), seriesColors = model.getSeriesColors();
if(iGroup == -1)
iGroup = 0;
// Number of segments
var nSeg = yValues[iGroup].length;
var total = 0;
for (var i = 0; i < nSeg; ++i)
{
total += yValues[iGroup][i];
}
var dataElems = this._dataElems, animate = (this._animDuration>0)
var pathElem = svgDoc.getElementById("funnelPathPrototype");
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(0.00001, 0.00001)";
var x = 0, y = 0, slope = (quadWidth/2)/quadHeight,
dx = quadWidth, dy, nextX, nextY;
for (var i = nSeg-1; i >= 0; --i)
{
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
var valueRatio = (yValues[iGroup][i])/(total);
var sb = new ApacheChartBuffer();
sb.append("M").append(x).append(",").append(y);
sb.append("L").append(dx).append(",").append(y);
dy = (quadHeight)*valueRatio;
nextY = y + dy;
nextX = quadWidth/2 - slope*(quadHeight-(nextY) );
dx = quadWidth - nextX;
if(i != 0)
{
sb.append("L").append(dx).append(",").append(nextY);
sb.append("L").append(nextX).append(",").append(nextY);
sb.append("Z");
}
else
{
var startTipY = (dy/3<=ApacheFunnelChart._MAX_FUNNEL_TIP)?y+(dy - dy/3):
quadHeight-ApacheFunnelChart._MAX_FUNNEL_TIP;
nextX = quadWidth/2 - slope*(quadHeight-(startTipY) );
dx = quadWidth - nextX;
sb.append("L").append(dx).append(",").append(startTipY);
sb.append("L").append(dx).append(",").append(quadHeight);
sb.append("L").append(nextX).append(",").append(quadHeight);
sb.append("L").append(nextX).append(",").append(startTipY);
sb.append("Z");
}
pathElem.setAttribute("d", sb.toString());
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("yValueIndex", iGroup);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
fnlContainer.appendChild(pathElem);
y = nextY;
x = nextX;
}
}
ApacheFunnelChart.prototype._drawPerspectiveFunnel = function(
fnlContainer, quadWidth,
quadHeight, iGroup)
{
var svgDoc = this._svgDoc, model = this._model, yValues = model.getYValues();
var groupLabels = model.getGroupLabels(), seriesColors = model.getSeriesColors();
if(iGroup == -1)
iGroup = 0;
// Number of segments
var nSeg = yValues[iGroup].length;
var total = 0;
for (var i = 0; i < nSeg; ++i)
{
total += yValues[iGroup][i];
}
var dataElems = this._dataElems, animate = (this._animDuration>0)
var pathElem = svgDoc.getElementById("funnelPathPrototype");
var gradientsUsed = this._gradientsUsed;
var defaultTransform = "scale(0.00001, 0.00001)";
var x = 0, y = 0, slope = (quadWidth/2)/quadHeight,
dx = quadWidth, dy, nextX, oldDx, nextY;
// the ring height is 1/12 of the width
var rx = dx/2, ry = dx/24, oldRx, oldRy;
for (var i = nSeg-1; i >= 0; --i)
{
pathElem = pathElem.cloneNode(false);
if(animate)
{
dataElems.push(pathElem);
pathElem.setAttribute("transform",defaultTransform);
}
var valueRatio = (yValues[iGroup][i])/(total);
var sb = new ApacheChartBuffer();
sb.append("M").append(x).append(",").append(y);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(dx).append(",").append(y);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(x).append(",").append(y);
oldDx = dx;
oldRx = rx;
oldRy = ry;
dy = (quadHeight)*valueRatio;
nextY = y + dy;
nextX = quadWidth/2 - slope*(quadHeight-(nextY) );
dx = quadWidth - nextX;
rx = (dx-nextX)/2;
ry = rx/12;
if(i != 0)
{
sb.append("L").append(nextX).append(",").append(nextY);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(dx).append(",").append(nextY);
sb.append("L").append(oldDx).append(",").append(y);
}
else
{
var startTipY = (dy/3<=ApacheFunnelChart._MAX_FUNNEL_TIP)?y+(dy - dy/3):
quadHeight-ApacheFunnelChart._MAX_FUNNEL_TIP;
nextX = quadWidth/2 - slope*(quadHeight-(startTipY) );
dx = quadWidth - nextX;
rx = (dx-nextX)/2;
ry = rx/12;
sb.append("L").append(nextX).append(",").append(startTipY);
sb.append("L").append(nextX).append(",").append(quadHeight);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(dx).append(",").append(quadHeight);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(nextX).append(",").append(quadHeight);
sb.append("A").append(rx).append(",").append(ry);
sb.append(" 0 1,0 ").append(dx).append(",").append(quadHeight);
sb.append("L").append(dx).append(",").append(startTipY);
sb.append("L").append(oldDx).append(",").append(y);
}
pathElem.setAttribute("d", sb.toString());
if(gradientsUsed)
pathElem.setAttribute("fill", "url(#gradient"+i+")");
else
pathElem.setAttribute("fill", seriesColors[i]);
pathElem.setAttribute("stroke", seriesColors[i]);
pathElem.setAttribute("stroke-width", 1);
pathElem.setAttribute("yValueIndex", iGroup);
pathElem.setAttribute("seriesIndex", i);
if(this._tooltipsVisible)
{
pathElem.addEventListener("mouseover",this.ShowToolTipCallback,false);
pathElem.addEventListener("mouseout",this.HideToolTipCallback,false);
}
pathElem.addEventListener("click",this.ClickCallback,false);
fnlContainer.appendChild(pathElem);
y = nextY;
x = nextX;
}
}
ApacheFunnelChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
var evtTarget = e.target;
var targetBBox = evtTarget.getBBox();
var ctm = evtTarget.parentNode.getCTM();
return {x:(ctm.e+targetBBox.x+targetBBox.width/2),
y:(ctm.f+targetBBox.y+targetBBox.height/2 - ttBBox.height)};
}
ApacheFunnelChart._MAX_FUNNEL_TIP = 16;
////////////////////////////////////////////////////////////////////
// Circular gauge chart subclass
////////////////////////////////////////////////////////////////////
function ApacheGaugeChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheChart, ApacheGaugeChart);
ApacheGaugeChart.prototype.Init = function(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
ApacheGaugeChart.superclass.Init.call(this, type, model, svgEmbedId,
isPerspective, legendPosition);
//this._markerTextGroup = undefined;
//this._animCx;
//this._animCy;
}
ApacheGaugeChart.prototype.SetDataAnimStep = function(ratio)
{
var animElems = this._dataElems, animCount = animElems.length;
var model = this._model, yValues = model.getYValues();
for(var i = 0; i < animCount; ++i)
{
// For Dial Chart only one value is applicable
var yValue = yValues[i][0];
this.SetIndicatorPosition(yValue, animElems[i], ratio);
}
}
ApacheGaugeChart.prototype.SetIndicatorPosition = function(yValue, indicator, ratio)
{
var theta, cx = this._animCx, cy = this._animCy;
var model = this._model, minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var valueRatio = ratio*(yValue - minValue)/(maxValue-minValue);
theta = Math.PI/6 + valueRatio*(5*Math.PI/3);
if(theta < Math.PI/2)
theta += 3*Math.PI/2;
else
theta -= Math.PI/2;
theta *= 180/Math.PI;
indicator.setAttribute("transform", "rotate("+theta+" "+cx+" "+cy+")");
}
ApacheGaugeChart.prototype.DrawChartData = function()
{
if(this._yMinorGridCount<0)
this._yMinorGridCount = 4;
var rootElem = this._rootElement;
// calculate the number of rows and columns
var model = this._model, yValues = model.getYValues(), yValueCount = yValues.length
var groupLabels = model.getGroupLabels(),
groupCount = groupLabels?groupLabels.length:1;
var nCols = Math.ceil(Math.sqrt(yValueCount)), nRows = Math.round(Math.sqrt(yValueCount));
var margins = this._margins, dx=margins.left, dy=margins.top;
var quadWidth = (this._width - margins.left - margins.right)/nCols;
var vGap = 2*ApacheChart._TEXT_MARGIN;
var quadHeight = (this._height - margins.top - margins.bottom - (nRows-1)*vGap)/nRows;
var labelElem = this._svgDoc.getElementById("groupLabelPrototype");
for(var i = 0; i= yValueCount)
break;
var groupLabel = (iGroup == -1)?null:groupLabels[iGroup];
var gaugeContainer = rootElem.cloneNode(false);
rootElem.appendChild(gaugeContainer);
if(groupLabel)
labelElem = labelElem.cloneNode(true);
var newHeight = this.DrawGroupLabelTitle(groupLabel, rootElem,
labelElem, dx, dy,
quadWidth, quadHeight);
var newWidth = quadWidth - 2*ApacheChart._TEXT_MARGIN;
this.DrawDial(gaugeContainer, newWidth, newHeight, iGroup);
gaugeContainer.setAttribute("transform",
"translate("+(dx+ApacheChart._TEXT_MARGIN)+","+dy+")");
if(groupLabel)
{
var gBBox = gaugeContainer.getBBox(), gHeight = gBBox.height;
if(gHeight < newHeight-vGap)
{
var newY = parseInt(labelElem.getAttribute("y"));
newY -= (newHeight-gHeight)/2-vGap;
labelElem.setAttribute("y", newY);
}
}
dx +=quadWidth;
}
dx=margins.left;
dy +=quadHeight+vGap;
}
}
ApacheGaugeChart.prototype.DrawLegend = function()
{
// Legend does not make sense for a gauge
}
ApacheGaugeChart.prototype.ComputeMinMaxValues = function()
{
}
ApacheGaugeChart.prototype.DrawGroupLabels = function()
{
}
ApacheGaugeChart.prototype.LayoutGroupLabels = function()
{
}
ApacheGaugeChart.prototype.DrawGrid = function()
{
}
ApacheGaugeChart.prototype.DrawYValueLabels = function()
{
}
ApacheGaugeChart.prototype.LayoutYValueLabels = function()
{
}
ApacheGaugeChart.prototype.DrawDial = function(
gaugeContainer, quadWidth,
quadHeight, iGroup)
{
var svgDoc = this._svgDoc, model = this._model;
if(iGroup == -1)
iGroup = 0;
var dataElems = this._dataElems, animate = (this._animDuration>0);
var templateName = this.GetGaugeTemplateName();
var gauge = svgDoc.getElementById(templateName).cloneNode(true);
gaugeContainer.appendChild(gauge);
var gaugeBBox = gauge.getBBox(), gaugeWidth = gaugeBBox.width,
gaugeR = gaugeWidth/2;
var gElem = this._markerTextGroup;
var groups = gauge.getElementsByTagName("g");
var indicator = groups.item(groups.length-1);
indicator.setAttribute("yValueIndex", iGroup);
indicator.setAttribute("seriesIndex", 0);
if(this._tooltipsVisible)
{
indicator.addEventListener("mouseover",this.ShowToolTipCallback,false);
indicator.addEventListener("mouseout",this.HideToolTipCallback,false);
}
indicator.addEventListener("click",this.ClickCallback,false);
if(this._animCx == null)
{
this._animCx = indicator.getAttribute("_pivotCenterX");
this._animCy = indicator.getAttribute("_pivotCenterY");
}
if(animate)
dataElems.push(indicator);
else
{
// If there is no animation lets move the indicators to the last position
this.SetIndicatorPosition(this._model.getYValues()[iGroup][0], indicator, 1);
}
if(gElem!=null)
{
gElem = gElem.cloneNode(true);
gauge.appendChild(gElem);
}
else
{
this.CreateTextMarkerGroup(gauge, gaugeR);
}
this.ScaleGauge(gauge, quadWidth, quadHeight, gaugeWidth, gaugeBBox.height);
}
ApacheGaugeChart.prototype.GetGaugeTemplateName = function()
{
return "circularGauge";
}
ApacheGaugeChart.prototype.CreateTextMarkerGroup = function(gauge, gaugeR)
{
var svgDoc = this._svgDoc, model = this._model;
var gElem = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
gauge.appendChild(gElem);
this._markerTextGroup = gElem;
var majorMarker = svgDoc.getElementById("gaugeMarkerMajor"),
majorMarkerCount = this._yMajorGridCount,
minorMarker = svgDoc.getElementById("gaugeMarkerMinor"),
minorMarkerCount = this._yMinorGridCount,
textElem = svgDoc.getElementById("gaugeTextPrototype");
var paths = gauge.getElementsByTagName("path");
var markerContainerR = parseInt(gauge.getAttribute("_markerRadius"));
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var x, y, angle, textMargin;
for(var i=0, theta = Math.PI/6;
i<=majorMarkerCount; ++i, theta += (5*Math.PI/3)/majorMarkerCount)
{
var adjustedTheta;
if(theta < Math.PI/2)
adjustedTheta = theta +3*Math.PI/2;
else
adjustedTheta = theta - Math.PI/2;
x = gaugeR-markerContainerR*(Math.cos(adjustedTheta));
y = gaugeR-markerContainerR*(Math.sin(adjustedTheta));
var angle = adjustedTheta*180/Math.PI;
var marker = majorMarker.cloneNode(true);
gElem.appendChild(marker);
marker.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+") rotate("+angle.toFixed(0)+" 0 0)");
var value = minValue + i*(maxValue-minValue)/(majorMarkerCount);
textElem = textElem.cloneNode(true);
textElem.firstChild.data = this._formatValue(value);
gElem.appendChild(textElem);
var textBBox = textElem.getBBox();
if(i == 0)
{
textMargin = textBBox.height/2;
}
x = gaugeR-(markerContainerR-textMargin)*(Math.cos(adjustedTheta));
y = gaugeR-(markerContainerR-textMargin)*(Math.sin(adjustedTheta));
if(theta >= 5*Math.PI/6 && theta <= 7*Math.PI/6)
{
y += textBBox.height;
x -= textBBox.width/2
}
else
{
y += textBBox.height/2;
if(theta < Math.PI)
x += 2*ApacheChart._TEXT_MARGIN;
else
x -= textBBox.width + 2*ApacheChart._TEXT_MARGIN;
}
textElem.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+")");
}
for(var i=(minorMarkerCount+1),
theta = Math.PI/6+(5*Math.PI/3)/(majorMarkerCount*minorMarkerCount);
i<=(majorMarkerCount+1)*minorMarkerCount;
++i, theta += (5*Math.PI/3)/(majorMarkerCount*minorMarkerCount))
{
if(i%minorMarkerCount == 0)
continue;
var adjustedTheta;
if(theta < Math.PI/2)
adjustedTheta = theta +3*Math.PI/2;
else
adjustedTheta = theta - Math.PI/2;
var x = gaugeR-markerContainerR*(Math.cos(adjustedTheta));
var y = gaugeR-markerContainerR*(Math.sin(adjustedTheta));
var angle = adjustedTheta*180/Math.PI;
var marker = minorMarker.cloneNode(true);
gElem.appendChild(marker);
marker.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+") rotate("+angle.toFixed(0)+" 0 0)");
}
}
ApacheGaugeChart.prototype.ScaleGauge = function(gauge, quadWidth, quadHeight, gaugeWidth, gaugeHeight)
{
var minSide = Math.min(quadWidth, quadHeight);
var tx = (minSide == quadWidth)?0: (quadWidth-minSide)/2,
ty = (minSide == quadHeight)?0: (quadHeight-minSide)/2;
var scale = minSide/gaugeWidth;
gauge.setAttribute("transform","translate("+tx+","+ty+") scale("+scale+","+scale+")");
}
ApacheGaugeChart.prototype.GetChartEvent = function(e)
{
var evtTarget = e.target;
while(evtTarget != null && evtTarget.tagName != "g")
evtTarget = evtTarget.parentNode;
if(evtTarget == null)
return null;
var i = parseInt(evtTarget.getAttribute("yValueIndex"));
var model = this._model, yValues = model.getYValues();
return new ApacheChartEvent(null,[i], [yValues[i][0]], null);
}
ApacheGaugeChart.prototype.FillToolTipData = function(boundingRectElem, circleElem, e)
{
var chartEvent = this.GetChartEvent(e);
if(chartEvent == null)
return;
var value = chartEvent.getYValues()[0];
var groupLabels = this._model.getGroupLabels();
var textElem = boundingRectElem.nextSibling.nextSibling;
textElem.firstChild.data = ""+this._formatValue(value);
var labelWidth = textElem.getBBox().width;
//We do not need the next label
textElem = textElem.nextSibling.nextSibling;
textElem.firstChild.data = "";
var rectWidth = labelWidth+2*ApacheChart._TEXT_MARGIN;
boundingRectElem.setAttribute("width",rectWidth);
boundingRectElem.setAttribute("stroke", seriesColors[0]);
circleElem.setAttribute("r",0);
}
ApacheGaugeChart.prototype.GetToolTipLocation = function(e, ttBBox)
{
return {x:(e.clientX+20), y:(e.clientY+20)};
}
////////////////////////////////////////////////////////////////////
// Semi-Circular gauge chart subclass
////////////////////////////////////////////////////////////////////
function ApacheSemiGaugeChart(
type, model, svgEmbedId,
isPerspective, legendPosition)
{
this.Init(type, model, svgEmbedId, isPerspective, legendPosition);
}
ApacheChartObj.Inherit(ApacheGaugeChart, ApacheSemiGaugeChart);
ApacheSemiGaugeChart.prototype.SetIndicatorPosition = function(yValue, indicator, ratio)
{
var theta, cx = this._animCx, cy = this._animCy;
var model = this._model, minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var valueRatio = ratio*(yValue - minValue)/(maxValue-minValue);
theta = valueRatio*Math.PI;
theta *= 180/Math.PI;
indicator.setAttribute("transform", "rotate("+theta+" "+cx+" "+cy+")");
}
ApacheSemiGaugeChart.prototype.GetGaugeTemplateName = function()
{
return "semiGauge";
}
ApacheSemiGaugeChart.prototype.CreateTextMarkerGroup = function(gauge, gaugeR)
{
var svgDoc = this._svgDoc, model = this._model;
gElem = svgDoc.createElementNS("http://www.w3.org/2000/svg", "g");
gauge.appendChild(gElem);
this._markerTextGroup = gElem;
var majorMarker = svgDoc.getElementById("gaugeMarkerMajor"),
majorMarkerCount = this._yMajorGridCount,
minorMarker = svgDoc.getElementById("gaugeMarkerMinor"),
minorMarkerCount = this._yMinorGridCount,
textElem = svgDoc.getElementById("gaugeTextPrototype");
var paths = gauge.getElementsByTagName("path");
var markerContainerR = parseInt(gauge.getAttribute("_markerRadius"));
var minValue = model.getMinYValue(), maxValue = model.getMaxYValue();
var x, y, angle, textMargin;
for(var i=0; i<=majorMarkerCount; ++i)
{
var theta = i*Math.PI/majorMarkerCount;
x = gaugeR-markerContainerR*(Math.cos(theta));
y = gaugeR-markerContainerR*(Math.sin(theta));
var angle = theta*180/Math.PI;
var marker = majorMarker.cloneNode(true);
gElem.appendChild(marker);
marker.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+") rotate("+angle.toFixed(0)+" 0 0)");
var value = minValue + i*(maxValue-minValue)/(majorMarkerCount);
textElem = textElem.cloneNode(true);
textElem.firstChild.data = this._formatValue(value);
gElem.appendChild(textElem);
var textBBox = textElem.getBBox();
if(i == 0)
{
textMargin = textBBox.height/2;
}
x = gaugeR-(markerContainerR-textMargin)*(Math.cos(theta));
y = gaugeR-(markerContainerR-textMargin)*(Math.sin(theta));
if(theta >= Math.PI/3 && theta <= 2*Math.PI/3)
{
y += textBBox.height;
x -= textBBox.width/2
}
else
{
y += textBBox.height/2;
if(theta < Math.PI/2)
x += 2*ApacheChart._TEXT_MARGIN;
else
x -= textBBox.width + 2*ApacheChart._TEXT_MARGIN;
}
textElem.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+")");
}
for(var i=1; i<=(majorMarkerCount)*minorMarkerCount; ++i)
{
if(i%minorMarkerCount == 0)
continue;
var theta = i*Math.PI/(majorMarkerCount*minorMarkerCount);
var x = gaugeR-markerContainerR*(Math.cos(theta));
var y = gaugeR-markerContainerR*(Math.sin(theta));
var angle = theta*180/Math.PI;
var marker = minorMarker.cloneNode(true);
gElem.appendChild(marker);
marker.setAttribute("transform",
"translate("+x.toFixed(0)+","+y.toFixed(0)+") rotate("+angle.toFixed(0)+" 0 0)");
}
}
ApacheSemiGaugeChart.prototype.ScaleGauge = function(
gauge,
quadWidth,
quadHeight,
gaugeWidth,
gaugeHeight)
{
var sx = quadWidth/gaugeWidth, sy = quadHeight/gaugeHeight;
var scale = Math.min(sx, sy);
var tx = (quadWidth<=gaugeWidth)?0:(quadWidth-gaugeWidth)/2,
ty = (quadHeight<=gaugeHeight)?0:(quadHeight-gaugeHeight)/2;
gauge.setAttribute("transform","translate("+tx+","+ty+") scale("+scale+","+scale+")");
}