You are viewing a plain text version of this content. The canonical link for it is here.
Posted to adffaces-commits@incubator.apache.org by aw...@apache.org on 2006/09/28 18:07:59 UTC

svn commit: r450952 [3/6] - in /incubator/adffaces/trunk/trinidad: src/site/xdoc/devguide/ trinidad-api/src/main/java-templates/org/apache/myfaces/trinidad/component/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/event/ trinidad-api/src/main/j...

Added: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/javascript/META-INF/adf/jsLibs/ApacheChart.js
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/javascript/META-INF/adf/jsLibs/ApacheChart.js?view=auto&rev=450952
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/javascript/META-INF/adf/jsLibs/ApacheChart.js (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/javascript/META-INF/adf/jsLibs/ApacheChart.js Thu Sep 28 11:07:57 2006
@@ -0,0 +1,5315 @@
+/////////////////////////////////////////////
+// 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;
+}
+
+ApacheChartObj.prototype.createCallback = function(func)
+{
+  // create a function that sets up "this" and delegates all of the parameters
+  // to the passed in function
+  var proxyFunction = new Function(
+    "var f=arguments.callee; return f._func.apply(f._owner, arguments);");
+
+  // attach ourselves as "this" to the created function
+  proxyFunction._owner = this;
+
+  // attach function to delegate to
+  proxyFunction._func = func;
+
+  return proxyFunction;
+}
+
+/**
+* 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;
+  
+  // the maximum value used to display the y-axis. 
+  // Default is 20% of maximum of the yValues
+  //this._maxYValue = undefined;
+  
+  // the minimum value used to display the y-axis. Default is 0
+  this._minYValue = 0;
+
+  // the maximum value used to display the X-axis. 
+  // Default is 20% of maximum of the xValues
+  //this._maxXValue = undefined;
+  
+  // the minimum value used to display the y-axis. Default is 0
+  this._minXValue = 0;
+    
+  // 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 = "<H4>Unable to load SVG plugin. Please install the plugin from <a href='#' onclick=\"window.open('http://www.adobe.com/svg/viewer/install/main.html')\">Adobe</a><H4>";
+  else
+    this._errorHtml = "<H4>This component needs an SVG enabled browser like Internet Explorer, Firefox 1.5+ or Opera 9.0+<H4>";
+  
+  this._statusHtml = "<H4>Please Wait. Attempting to load SVG document...</H4>";
+  this.ComputeMaxValues();
+}
+
+/**
+ * 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.ComputeMaxValues = function()
+{
+  var model = this._model, yValues = model.getYValues(), xValues = model.getXValues(),
+      maxYValue = model.getMaxYValue(), maxXValue = model.getMaxXValue(),
+      seriesLabels = model.getSeriesLabels();
+  if(yValues != null && maxYValue == null)
+    model.setMaxYValue(this._computeAxisMaxValues(yValues, seriesLabels.length));
+  
+  if(xValues != null && maxXValue == null)
+    model.setMaxXValue(this._computeAxisMaxValues(xValues, seriesLabels.length));
+}
+
+ApacheChart.prototype._computeAxisMaxValues = function(values, seriesSize)
+{
+  var stackedTotal, maxValue = 0, 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)
+    {
+      if (isStacked)
+        stackedTotal += values[i][j];
+      else
+        maxValue = Math.max(maxValue, values[i][j]);
+    }
+    if (isStacked)
+      maxValue = Math.max(maxValue, stackedTotal);
+  }
+  return maxValue*ApacheChart._MAX_MULTIPLIER;
+}
+
+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._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 = this.createCallback(this.ShowToolTip);
+    this.HideToolTipCallback = this.createCallback(this.HideToolTip);
+  }
+  this.ClickCallback = this.createCallback(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 = this.createCallback(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 = this.createCallback(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 gradientTemplate = gradients.childNodes[0], gradientElement;
+   
+  for (var i = 0; i< seriesCount; ++i)
+  {
+    gradientElement = svgDoc.getElementById("gradient"+i);
+    if(gradientElement == null)
+    {
+      gradientElement = gradientTemplate.cloneNode(true);
+      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 this._to_hex(val);  
+}
+
+ApacheChart.prototype._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.getComputedTextLength();
+    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.getComputedTextLength()+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.getComputedTextLength(), 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 textWidth = labelElem.getComputedTextLength();
+  if(quadWidth > textWidth)
+		dx += (quadWidth-textWidth)/2;
+	else
+	  dx += 2;
+  labelElem.setAttribute("x", dx);
+	
+	if(animate)
+	  labelElems.push(labelElem);
+	
+  var rect = labelElem.getBBox();
+  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.getComputedTextLength();      
+  //actual value
+  textElem = textElem.nextSibling.nextSibling;
+  textElem.firstChild.data = this._formatValue(yValues[0]);
+  var dataWidth = textElem.getComputedTextLength();
+  // 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("dx",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.getComputedTextLength();
+    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<nRows; ++i)
+  {
+    for(var j = 0; j<nCols; ++j)
+    {  
+      var iGroup = groupLabels?(i*nCols + j):(-1);
+      if(iGroup >= 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.ComputeMaxValues = 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; j<nPies; ++j)
+    {
+      var angle = pieAnimAngles[agleIndex++]*2*Math.PI;
+      var tx = radius*Math.sin(angle), ty = radius*Math.cos(angle);
+      if(angle <= Math.PI/2)
+      {
+        ty = -ty;
+        tx = tx;
+      }
+      else if(angle <= Math.PI)
+      {
+        ;
+      }
+      else if(angle <= 3*Math.PI/2)
+      {
+        tx = -tx;
+      }
+      else
+      {
+        ty = -ty;
+        tx = -tx;
+      }
+      animElems[elemIndex++].setAttribute("transform", "translate("+tx+","+ty+")");
+      if(isPerspective)
+      {
+        animElems[elemIndex++].setAttribute("transform", "translate("+tx+","+ty+")");
+        animElems[elemIndex++].setAttribute("transform", "translate("+tx+","+ty+")");
+      }
+    }
+  }
+}
+
+ApachePieChart.prototype._drawPies = 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);
+  
+  if(iGroup == -1)
+    iGroup = 0;
+
+  var nPies = yValues[iGroup].length;
+  var pieTotal = 0;
+    
+  for (var i = 0; i < nPies; ++i)
+  {
+    pieTotal += yValues[iGroup][i];
+  }
+  
+  var pathElem = svgDoc.getElementById("piePathPrototype"), pieStart = 0, animAngleStart = 0;
+  var pieElems = new Array(nPies), dataElems = this._dataElems, animate = (this._animDuration>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);
+      var curAnimRatio = (yValues[iGroup][i])/(pieTotal);
+      pieAnimAngles.push(animAngleStart+curAnimRatio/2);
+      animAngleStart+=curAnimRatio;
+    }
+    var sb = new ApacheChartBuffer();
+    sb.append("M0,0L");
+    sb.append(pieSize*Math.cos(pieStart*Math.PI*2));
+    sb.append(",").append(pieSize*Math.sin(pieStart*Math.PI*2));
+
+    if (valueRatio >= .5) // major arc
+    {
+      sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 0 0 ");
+      sb.append(pieSize* Math.cos((pieStart+valueRatio)*Math.PI*2));
+      sb.append(",").append(pieSize*Math.sin((pieStart+valueRatio)*Math.PI*2));
+    }
+    else
+    {
+      sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 0 ");
+      sb.append(pieSize* Math.cos((pieStart+valueRatio)*Math.PI*2));
+      sb.append(",").append(pieSize*Math.sin((pieStart+valueRatio)*Math.PI*2));
+    }
+    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);
+    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 ");
+      sb.append(arcEndX).append(",").append(arcEndY);
+    }
+    else
+    {
+      sb.append("A").append(pieSize).append(" ").append(pieSize).append(" 1 1 0 ");
+      sb.append(arcEndX).append(",").append(arcEndY);
+    }
+    sb.append("z");
+    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 ");
+      sb.append(arcEndX).append(",").append(arcEndY);
+    }
+    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 ");
+      sb.append(arcBeginX).append(",").append(arcBeginY+perspectiveHeight);
+    }
+    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 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)};
+}
+
+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);

[... 2542 lines stripped ...]