All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.adf.jsLibsDebug.ApacheChart.js Maven / Gradle / Ivy

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+")"); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy