Draws the x and y axis on the svg object within their
respective groups
@private
321639
function drawAxis(){
            svg.select('.x-axis-group.axis')
                .attr('transform', `translate(0, ${chartHeight})`)
                .call(xAxis);

            svg.selectAll('.x-axis-group .tick text')
                .style('text-anchor', 'start')
                .attr('transform', 'rotate(45 -1 10)');

            if (xAxisLabel) {
                if (xAxisLabelEl) {
                    svg.selectAll('.x-axis-label-text').remove();
                }
                xAxisLabelEl = svg.select('.x-axis-label')
                  .append('text')
                    .attr('y', xAxisLabelOffset)
                    .attr('text-anchor', 'middle')
                    .classed('x-axis-label-text', true)
                    .attr('x', chartWidth / 2)
                    .text(xAxisLabel);
            }

            svg.select('.y-axis-group.axis')
                .call(yAxis);

            if (yAxisLabel) {
                if (yAxisLabelEl) {
                    svg.selectAll('.y-axis-label-text').remove();
                }
                yAxisLabelEl = svg.select('.y-axis-label')
                  .append('text')
                    .classed('y-axis-label-text', true)
                    .attr('x', -chartHeight / 2)
                    .attr('y', yAxisLabelOffset)
                    .attr('text-anchor', 'middle')
                    .attr('transform', 'rotate(270 0 0)')
                    .text(yAxisLabel);
            }
        }
1345281
function () {
		var options = this.options,
			chart = this.chart,
			group = this.group,
			title = this.title,
			shape = this.shape,
			linkedTo = this.linkedObject,
			xAxis = chart.xAxis[options.xAxis],
			yAxis = chart.yAxis[options.yAxis],
			width = options.width,
			height = options.height,
			anchorY = ALIGN_FACTOR[options.anchorY],
			anchorX = ALIGN_FACTOR[options.anchorX],
			resetBBox = false,
			shapeParams,
			linkType,
			series,
			param,
			bbox,
			x,
			y;

		if (linkedTo) {
			linkType = (linkedTo instanceof Highcharts.Point) ? 'point' :
						(linkedTo instanceof Highcharts.Series) ? 'series' : null;

			if (linkType === 'point') {
				options.xValue = linkedTo.x;
				options.yValue = linkedTo.y;
				series = linkedTo.series;
			} else if (linkType === 'series') {
				series = linkedTo;
			}

			if (group.visibility !== series.group.visibility) {
				group.attr({
					visibility: series.group.visibility
				});
			}
		}


		// Based on given options find annotation pixel position
		x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x);
		y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y;

		if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) {
			return;
		}


		if (title) {
			title.attr(options.title);
			title.css(options.title.style);
			resetBBox = true;
		}

		if (shape) {
			shapeParams = extend({}, options.shape.params);

			if (options.units === 'values') {
				for (param in shapeParams) {
					if (inArray(param, ['width', 'x']) > -1) {
						shapeParams[param] = xAxis.translate(shapeParams[param]);
					} else if (inArray(param, ['height', 'y']) > -1) {
						shapeParams[param] = yAxis.translate(shapeParams[param]);
					}
				}

				if (shapeParams.width) {
					shapeParams.width -= xAxis.toPixels(0) - xAxis.left;
				}

				if (shapeParams.x) {
					shapeParams.x += xAxis.minPixelPadding;
				}

				if (options.shape.type === 'path') {
					translatePath(shapeParams.d, xAxis, yAxis, x, y);
				}
			}

			// move the center of the circle to shape x/y
			if (options.shape.type === 'circle') {
				shapeParams.x += shapeParams.r;
				shapeParams.y += shapeParams.r;
			}

			resetBBox = true;
			shape.attr(shapeParams);
		}

		group.bBox = null;

		// If annotation width or height is not defined in options use bounding box size
		if (!isNumber(width)) {
			bbox = group.getBBox();
			width = bbox.width;
		}

		if (!isNumber(height)) {
			// get bbox only if it wasn't set before
			if (!bbox) {
				bbox = group.getBBox();
			}

			height = bbox.height;
		}

		// Calculate anchor point
		if (!isNumber(anchorX)) {
			anchorX = ALIGN_FACTOR.center;
		}

		if (!isNumber(anchorY)) {
			anchorY = ALIGN_FACTOR.center;
		}

		// Translate group according to its dimension and anchor point
		x = x - width * anchorX;
		y = y - height * anchorY;

		if (chart.animation && defined(group.translateX) && defined(group.translateY)) {
			group.animate({
				translateX: x,
				translateY: y
			});
		} else {
			group.translate(x, y);
		}
	}
909656
function drawHorizontalGridLines() {
            maskGridLines = svg.select('.grid-lines-group')
                .selectAll('line.horizontal-grid-line')
                .data(yScale.ticks(yTicks))
                .enter()
                  .append('line')
                    .attr('class', 'horizontal-grid-line')
                    .attr('x1', xAxisPadding.left)
                    .attr('x2', chartWidth)
                    .attr('y1', (d) => yScale(d))
                    .attr('y2', (d) => yScale(d));
        }
103061
function drawDataReferencePoints() {
            // Creates Dots on Data points
            var points = svg.select('.chart-group').selectAll('.dots')
                .data(layers)
                .enter()
                  .append('g')
                    .attr('class', 'dots')
                    .attr('d', ({values}) => area(values))
                    .attr('clip-path', 'url(#clip)');

            // Processes the points
            // TODO: Optimize this code
            points.selectAll('.dot')
                .data(({values}, index) => values.map((point) => ({index, point})))
                .enter()
                  .append('circle')
                    .attr('class','dot')
                    .attr('r', () => pointsSize)
                    .attr('fill', () => pointsColor)
                    .attr('stroke-width', '0')
                    .attr('stroke', pointsBorderColor)
                    .attr('transform', function(d) {
                        let {point} = d;
                        let key = xScale(point.date);

                        dataPoints[key] = dataPoints[key] || [];
                        dataPoints[key].push(d);

                        let {date, y, y0} = point;

                        return `translate( ${xScale(date)}, ${yScale(y + y0)} )`;
                    });
        }
1182602
def _y_axis(self, draw_axes=True):
        """"""
        axis = self.svg.node(self.nodes['plot'], class_="axis x gauge")

        for i, (label, theta) in enumerate(self._y_labels):
            guides = self.svg.node(axis, class_='guides')

            self.svg.line(
                guides, [self.view((.95, theta)),
                         self.view((1, theta))],
                close=True,
                class_='line'
            )

            self.svg.line(
                guides, [self.view((0, theta)),
                         self.view((.95, theta))],
                close=True,
                class_='guide line %s' %
                ('major' if i in (0, len(self._y_labels) - 1) else '')
            )

            x, y = self.view((.9, theta))
            self.svg.node(guides, 'text', x=x, y=y).text = label

            self.svg.node(
                guides,
                'title',
            ).text = self._y_format(theta)
537969
function () {
            var series = this,
              pointAttr,
              points = series.points,
              chart = series.chart,
              plotX,
              plotY,
              i,
              point,
              radius,
              symbol,
              isImage,
              graphic,
              options = series.options,
              seriesMarkerOptions = options.marker,
              seriesPointAttr = series.pointAttr[''],
              pointMarkerOptions,
              hasPointMarker,
              enabled,
              isInside,
              markerGroup = series.markerGroup,
              globallyEnabled = pick(
                seriesMarkerOptions.enabled,
                !series.requireSorting || series.activePointCount < (0.5 * series.xAxis.len / seriesMarkerOptions.radius)
              );

            if (seriesMarkerOptions.enabled !== false || series._hasPointMarkers) {

                i = points.length;
                while (i--) {
                    point = points[i];
                    plotX = mathFloor(point.plotX); // #1843
                    plotY = point.plotY;
                    graphic = point.graphic;
                    pointMarkerOptions = point.marker || {};
                    hasPointMarker = !!point.marker;
                    enabled = (globallyEnabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled;
                    isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858

                    // only draw the point if y is defined
                    if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {

                        // shortcuts
                        pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] || seriesPointAttr;
                        radius = pointAttr.r;
                        symbol = pick(pointMarkerOptions.symbol, series.symbol);
                        isImage = symbol.indexOf('url') === 0;

                        if (graphic) { // update
                            graphic[isInside ? 'show' : 'hide'](true) // Since the marker group isn't clipped, each individual marker must be toggled
                              .animate(extend({
                                  x: plotX - radius,
                                  y: plotY - radius
                              }, graphic.symbolName ? { // don't apply to image symbols #507
                                  width: 2 * radius,
                                  height: 2 * radius
                              } : {}));
                        } else if (isInside && (radius > 0 || isImage)) {
                            point.graphic = graphic = chart.renderer.symbol(
                              symbol,
                                plotX - radius,
                                plotY - radius,
                                2 * radius,
                                2 * radius,
                              hasPointMarker ? pointMarkerOptions : seriesMarkerOptions
                              )
                              .attr(pointAttr)
                              .add(markerGroup);
                        }

                    } else if (graphic) {
                        point.graphic = graphic.destroy(); // #1269
                    }
                }
            }

        }
512841
function (str, x, y) {
            var wrapper = this.createElement('span'),
              element = wrapper.element,
              renderer = wrapper.renderer;

            // Text setter
            wrapper.textSetter = function (value) {
                if (value !== element.innerHTML) {
                    delete this.bBox;
                }
                element.innerHTML = this.textStr = value;
            };

            // Various setters which rely on update transform
            wrapper.xSetter = wrapper.ySetter = wrapper.alignSetter = wrapper.rotationSetter = function (value, key) {
                if (key === 'align') {
                    key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML.
                }
                wrapper[key] = value;
                wrapper.htmlUpdateTransform();
            };

            // Set the default attributes
            wrapper.attr({
                  text: str,
                  x: mathRound(x),
                  y: mathRound(y)
              })
              .css({
                  position: ABSOLUTE,
                  whiteSpace: 'nowrap',
                  fontFamily: this.style.fontFamily,
                  fontSize: this.style.fontSize
              });

            // Use the HTML specific .css method
            wrapper.css = wrapper.htmlCss;

            // This is specific for HTML within SVG
            if (renderer.isSVG) {
                wrapper.add = function (svgGroupWrapper) {

                    var htmlGroup,
                      container = renderer.box.parentNode,
                      parentGroup,
                      parents = [];

                    this.parentGroup = svgGroupWrapper;

                    // Create a mock group to hold the HTML elements
                    if (svgGroupWrapper) {
                        htmlGroup = svgGroupWrapper.div;
                        if (!htmlGroup) {

                            // Read the parent chain into an array and read from top down
                            parentGroup = svgGroupWrapper;
                            while (parentGroup) {

                                parents.push(parentGroup);

                                // Move up to the next parent group
                                parentGroup = parentGroup.parentGroup;
                            }

                            // Ensure dynamically updating position when any parent is translated
                            each(parents.reverse(), function (parentGroup) {
                                var htmlGroupStyle;

                                // Create a HTML div and append it to the parent div to emulate
                                // the SVG group structure
                                htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, {
                                      className: attr(parentGroup.element, 'class')
                                  }, {
                                      position: ABSOLUTE,
                                      left: (parentGroup.translateX || 0) + PX,
                                      top: (parentGroup.translateY || 0) + PX
                                  }, htmlGroup || container); // the top group is appended to container

                                // Shortcut
                                htmlGroupStyle = htmlGroup.style;

                                // Set listeners to update the HTML div's position whenever the SVG group
                                // position is changed
                                extend(parentGroup, {
                                    translateXSetter: function (value, key) {
                                        htmlGroupStyle.left = value + PX;
                                        parentGroup[key] = value;
                                        parentGroup.doTransform = true;
                                    },
                                    translateYSetter: function (value, key) {
                                        htmlGroupStyle.top = value + PX;
                                        parentGroup[key] = value;
                                        parentGroup.doTransform = true;
                                    },
                                    visibilitySetter: function (value, key) {
                                        htmlGroupStyle[key] = value;
                                    }
                                });
                            });

                        }
                    } else {
                        htmlGroup = container;
                    }

                    htmlGroup.appendChild(element);

                    // Shared with VML:
                    wrapper.added = true;
                    if (wrapper.alignOnAdd) {
                        wrapper.htmlUpdateTransform();
                    }

                    return wrapper;
                };
            }
            return wrapper;
        }
415585
function(svg, options) {

    // the svg element to render charts to
    this._svg = svg;
    this._attributes = options || {};

    // outer margins
    this._margin = {
        top: 20,
        bottom: 75,
        left: this._attributes.yScales.primary.displayOnAxis === 'left' ? 75 : 25,
        right: this._attributes.yScales.primary.displayOnAxis === 'left' ? 25 : 75
    };

}
186451
function drawStackedAreas() {
            // Not ideal, we need to figure out how to call exit for nested elements
            if (series) {
                svg.selectAll('.layer-container').remove();
                svg.selectAll('.layer').remove();
                svg.selectAll('.area-outline').remove();
            }

            if (isUsingFakeData) {
                drawEmptyDataLine();

                return;
            }

            area = d3Shape.area()
                .curve(curveMap[areaCurve])
                .x(({data})=> xScale(data.date))
                .y0((d) => yScale(d[0]))
                .y1((d) => yScale(d[1]));

            areaOutline = d3Shape.line()
                .curve(area.curve())
                .x(({data}) => xScale(data.date))
                .y((d) => yScale(d[1]));

            if (isAnimated) {
                series = svg.select('.chart-group').selectAll('.layer')
                    .data(layersInitial, getName)
                    .enter()
                      .append('g')
                        .classed('layer-container', true);

                series
                  .append('path')
                    .attr('class', 'layer')
                    .attr('d', area)
                    .style('opacity', areaOpacity)
                    .style('fill', ({key}) => categoryColorMap[key]);

                series
                  .append('path')
                    .attr('class', 'area-outline')
                    .attr('d', areaOutline)
                    .style('stroke', ({key}) => categoryColorMap[key]);

                // Update
                svg.select('.chart-group').selectAll('.layer')
                    .data(layers)
                    .transition()
                    .delay((_, i) => areaAnimationDelays[i])
                    .duration(areaAnimationDuration)
                    .ease(ease)
                    .attr('d', area)
                    .style('opacity', areaOpacity)
                    .style('fill', ({key}) => categoryColorMap[key]);

                svg.select('.chart-group').selectAll('.area-outline')
                    .data(layers)
                    .transition()
                    .delay( (_, i) => areaAnimationDelays[i])
                    .duration(areaAnimationDuration)
                    .ease(ease)
                    .attr('d', areaOutline);

            } else {
                series = svg.select('.chart-group').selectAll('.layer')
                    .data(layers)
                    .enter()
                      .append('g')
                        .classed('layer-container', true);

                series
                  .append('path')
                    .attr('class', 'layer')
                    .attr('d', area)
                    .style('opacity', areaOpacity)
                    .style('fill', ({key}) => categoryColorMap[key]);

                series
                  .append('path')
                    .attr('class', 'area-outline')
                    .attr('d', areaOutline)
                    .style('stroke', ({key}) => categoryColorMap[key]);


                // Update
                svg.select('.chart-group').selectAll('.layer')
                    .attr('d', area)
                    .style('opacity', areaOpacity)
                    .style('fill', ({key}) => categoryColorMap[key]);

                svg.select('.chart-group').selectAll('.area-outline')
                    .attr('class', 'area-outline')
                    .attr('d', areaOutline)
                    .style('stroke', ({key}) => categoryColorMap[key]);
            }

            if (!hasOutline) {
                svg.select('.chart-group')
                  .selectAll('.area-outline')
                  .style('display', 'none');
            }

            // Exit
            series.exit()
                .transition()
                .style('opacity', 0)
                .remove();
        }
213824
function () {
		var series = this,
			options = this.options,
			props = [['graph', options.lineColor || this.color]],
			lineWidth = options.lineWidth,
			dashStyle =  options.dashStyle,
			graphPath = this.getGraphPath(),
			negativeColor = options.negativeColor;
			
		if (negativeColor) {
			props.push(['graphNeg', negativeColor]);
		}
		
		// draw the graph
		each(props, function (prop, i) {
			var graphKey = prop[0],
				graph = series[graphKey],
				attribs;
			
			if (graph) {
				stop(graph); // cancel running animations, #459
				graph.animate({ d: graphPath });
	
			} else if (lineWidth && graphPath.length) { // #1487
				attribs = {
					stroke: prop[1],
					'stroke-width': lineWidth,
					zIndex: 1 // #1069
				};
				if (dashStyle) {
					attribs.dashstyle = dashStyle;
				} else {
					attribs['stroke-linecap'] = attribs['stroke-linejoin'] = 'round';
				}

				series[graphKey] = series.chart.renderer.path(graphPath)
					.attr(attribs)
					.add(series.group)
					.shadow(!i && options.shadow);
			}
		});
	}
302722
function drawItemLabels(legend, barGroup) {
  // draw a subtle tick to help indicate what the label refers to
  function drawTick(y) {
    barGroup.appendChild(
      svgElement(
        legend,
        "line",
        {
          x1: legend.itemWidth,
          x2: legend.itemWidth + 5,
          y1: y,
          y2: y
        },
        "tick-mark"
      )
    );
  }

  function drawLabel(y, text) {
    var textOffsetX = 7;
    var textOffsetY = 3; // pixel shuffling to get the text to line up just right.
    barGroup.appendChild(
      svgElement(
        legend,
        "text",
        {
          x: legend.itemWidth + textOffsetX,
          y: y + textOffsetY
        },
        "item-label" + (legend.items.length > 6 ? "-small" : ""),
        text
      )
    );
  }

  legend.items.forEach(function(item, i) {
    var y = itemY(legend, i);
    if (defined(item.titleAbove)) {
      drawLabel(y, item.titleAbove);
      drawTick(y);
    }
    if (defined(item.title)) {
      drawLabel(y + legend.computedItemHeight / 2, item.title);
    }
    if (defined(item.titleBelow)) {
      drawLabel(y + legend.computedItemHeight, item.titleBelow);
      drawTick(y + legend.computedItemHeight);
    }
  });

  return itemY(legend, -1);
}
232313
function drawGridLines() {
            let scale = isHorizontal ? xScale : yScale;

            svg.select('.grid-lines-group')
                .selectAll('line')
                .remove();

            if (grid === 'horizontal' || grid === 'full') {
                svg.select('.grid-lines-group')
                    .selectAll('line.horizontal-grid-line')
                    .data(scale.ticks(yTicks).slice(1))
                    .enter()
                      .append('line')
                        .attr('class', 'horizontal-grid-line')
                        .attr('x1', (-xAxisPadding.left + 1))
                        .attr('x2', chartWidth)
                        .attr('y1', (d) => yScale(d))
                        .attr('y2', (d) => yScale(d));
            }

            if (grid === 'vertical' || grid === 'full') {
                svg.select('.grid-lines-group')
                    .selectAll('line.vertical-grid-line')
                    .data(scale.ticks(xTicks).slice(1))
                    .enter()
                      .append('line')
                        .attr('class', 'vertical-grid-line')
                        .attr('y1', 0)
                        .attr('y2', chartHeight)
                        .attr('x1', (d) => xScale(d))
                        .attr('x2', (d) => xScale(d));
            }

            if (isHorizontal) {
                drawVerticalExtendedLine();
            } else {
                drawHorizontalExtendedLine();
            }
        }
1010275
function drawItemBoxes(legend, barGroup) {
  legend.items.forEach(function(item, i) {
    var itemTop = itemY(legend, i);

    if (defined(item.imageUrl)) {
      barGroup.appendChild(
        svgElement(
          legend,
          "image",
          {
            "xlink:href": item.imageUrl,
            x: 0,
            y: itemTop,
            width: Math.min(item.imageWidth, legend.itemWidth + 4), // let them overlap slightly
            height: Math.min(item.imageHeight, legend.computedItemHeight + 4)
          },
          "item-icon"
        )
      );
      return;
    }

    if (defined(item.multipleColors)) {
      var columns = Math.sqrt(item.multipleColors.length) | 0;
      var rows = Math.ceil(item.multipleColors.length / columns) | 0;

      var colorCount = item.multipleColors.length;
      var index = 0;
      var y = itemTop;

      for (var row = 0; index < colorCount && row < rows; ++row) {
        var height =
          row === rows - 1
            ? legend.computedItemHeight - (y - itemTop)
            : legend.computedItemHeight / rows;

        var x = 0;
        for (var column = 0; index < colorCount && column < columns; ++column) {
          var color = item.multipleColors[index++];

          var width =
            column === columns - 1
              ? legend.itemWidth - x
              : legend.itemWidth / columns;

          barGroup.appendChild(
            svgElement(
              legend,
              "rect",
              {
                fill: color,
                x: x,
                y: y,
                width: width,
                height: height
              },
              "item-box"
            )
          );

          x += width;
        }

        y += height;
      }
    } else if (defined(item.color)) {
      const rectAttributes = {
        fill: item.color,
        x: 0,
        y: itemTop,
        width: legend.itemWidth,
        height: legend.computedItemHeight
      };
      if (defined(item.lineColor)) {
        rectAttributes.stroke = item.lineColor;
      }
      barGroup.appendChild(
        svgElement(legend, "rect", rectAttributes, "item-box")
      );
    }
  });
}
1271059
function drawHorizontalLegend() {
            let xOffset = markerSize;

            svg.select('.legend-group')
                .selectAll('g')
                .remove();

            // We want a single line
            svg.select('.legend-group')
              .append('g')
                .classed('legend-line', true);

            // And one entry per data item
            entries = svg.select('.legend-line')
              .selectAll('g.legend-entry')
              .data(data);

            // Enter
            entries.enter()
              .append('g')
                .classed('legend-entry', true)
                .attr('data-item', getId)
                .attr('transform', function({name}) {
                    let horizontalOffset = xOffset,
                        lineHeight = chartHeight / 2,
                        verticalOffset = lineHeight,
                        labelWidth = textHelper.getTextWidth(name, textSize);

                    xOffset += markerSize + 2 * getLineElementMargin() + labelWidth;

                    return `translate(${horizontalOffset},${verticalOffset})`;
                })
                .merge(entries)
              .append('circle')
                .classed('legend-circle', true)
                .attr('cx', markerSize/2)
                .attr('cy', markerYOffset)
                .attr('r', markerSize / 2)
                .style('fill', getCircleFill)
                .style('stroke-width', 1);

            svg.select('.legend-group')
                .selectAll('g.legend-entry')
              .append('text')
                .classed('legend-entry-name', true)
                .text(getName)
                .attr('x', getLineElementMargin())
                .style('font-size', `${textSize}px`)
                .style('letter-spacing', `${textLetterSpacing}px`);

            // Exit
            svg.select('.legend-group')
                .selectAll('g.legend-entry')
                .exit()
                .transition()
                .style('opacity', 0)
                .remove();

            adjustLines();
        }
576558
def svg(*args, **kw):
    
    class svg(type(helper.svg())):
        
        def __init__(self, *args, **kw):
            super(self.__class__, self).__init__(*args, **kw)
            # cartesian grid on/off
            self.__dict__['grid'] = False
            # axes on/off
            self.__dict__['axes'] = False
            # show origin
            self.__dict__['origin'] = False
            # grid aspects
            self.__dict__['grid_aspects'] = (4, 8)
            # x
            self.__dict__['x'] = 200
            # y
            self.__dict__['y'] = 200
            # elements
            self.__dict__['elements'] = []
            # size of the svg canvas, width and height
            self.__dict__['size'] = (100, 100)
            # grid id
            self.__dict__['grid_id'] = "gid%s" % id(self)
            # grid item id
            self.__dict__['grid_item_id'] = "gitem%s" % id(self)
            # grid width
            width = kw['width'] if kw.has_key('width') else self.__dict__['x']*2
            # grid height
            height = kw['height'] if kw.has_key('height') else self.__dict__['y']*2
            self.set_size(width, height)
        
        def set_grid(self, grid = True):
            self.__dict__['grid'] = grid
            return self
        
        def set_axes(self, axes = True):
            self.__dict__['axes'] = axes
            return self
        
        def set_origin(self, origin = True):
            self.__dict__['origin'] = origin
            return self
        
        def set_size(self, width, height):
            self.__dict__['x'] = width/2
            self.__dict__['y'] = height/2
            self.__dict__['size'] = (width, height)
            self.width = width
            self.height = height
            self.viewbox = "0 0 %s %s" % (width+1, height+1)
            return self
        
        def set_text(self, *args, **kw):
            kw['x'] = self.__dict__['x']+kw['x'] if kw.has_key('x') else self.__dict__['x'] 
            kw['y'] = self.__dict__['y']-kw['y'] if kw.has_key('y') else self.__dict__['y']
            self.__dict__['elements'].append(helper.text(*args, **kw))
            return self
        
        def set_rectangle(self, *args, **kw):
            kw['x'] = self.__dict__['x']+kw['x'] if kw.has_key('x') else self.__dict__['x']
            kw['y'] = self.__dict__['y']-kw['y'] if kw.has_key('y') else self.__dict__['y']
            self.__dict__['elements'].append(helper.rect(*args, **kw))
            return self
        
        def set_group(self, *args, **kw):
            self.__dict__['elements'].append(helper.g(*args, **kw))
            return self
        
        def set_defs(self, *args, **kw):
            self.__dict__['elements'].append(helper.defs(*args, **kw))
            return self
        
        def set_line(self, *args, **kw):
            kw['x1'] = self.__dict__['x']+kw['x1'] if kw.has_key('x1') else self.__dict__['x']
            kw['y1'] = self.__dict__['y']-kw['y1'] if kw.has_key('y1') else self.__dict__['y']
            kw['x2'] = self.__dict__['x']+kw['x2'] if kw.has_key('x2') else self.__dict__['x'] + 10
            kw['y2'] = self.__dict__['y']-kw['y2'] if kw.has_key('y2') else self.__dict__['y'] + 10
            self.__dict__['elements'].append(helper.line(*args, **kw))
            return self
        
        def set_circle(self, *args, **kw):
            kw['cx'] = self.__dict__['x']+kw['cx'] if kw.has_key('cx') else self.__dict__['x']
            kw['cy'] = self.__dict__['y']-kw['cy'] if kw.has_key('cy') else self.__dict__['y']
            self.__dict__['elements'].append(helper.circle(*args, **kw))
            return self
        
        def set_triangle(self, *args, **kw):
            kw['sides'] = 3
            return self.set_regular_polygon(*args, **kw)
        
        def set_square(self, *args, **kw):
            kw['sides'] = 4
            return self.set_regular_polygon(*args, **kw)
        
        def set_pentagon(self, *args, **kw):
            kw['sides'] = 5
            return self.set_regular_polygon(*args, **kw)
        
        def set_hexagon(self, *args, **kw):
            kw['sides'] = 6
            return self.set_regular_polygon(*args, **kw)
        
        def set_regular_polygon(self, *args, **kw):
            # set default properties for vertex
            kw['cx'] = kw['cx']+self.__dict__['x'] if kw.has_key('cx') else self.__dict__['x']
            kw['cy'] = kw['cy']-self.__dict__['y'] if kw.has_key('cy') else self.__dict__['y']
            kw['radius'] = kw['radius'] if kw.has_key('radius') else 1
            kw['sides'] = kw['sides'] if kw.has_key('sides') else 4
            kw['degrees'] = kw['degrees'] if kw.has_key('degrees') else 0
            # set generate polygon points
            points = self.polygon_points(self.vertex(cx=kw['cx'],
                                                     cy=kw['cy'],
                                                     radius=kw['radius'],
                                                     sides=kw['sides'],
                                                     degrees=kw['degrees']))
            # delete default properties
            del kw['cx']
            del kw['cy']
            del kw['radius']
            del kw['sides']
            del kw['degrees']
            # create polygon with given points and keywords
            self.__dict__['elements'].append(helper.polygon(points=points, *args, **kw))
            return self
        
        def polygon_points(self, vertex):
            return ' '.join(['%s,%s' % (point[0], point[1]) for point in vertex])
        
        def vertex(self, cx = 0, cy = 0, sides = 4, radius = 1, degrees = 0):
            centerAng = 2*pi/sides
            startAng = pi*degrees/180
            points = []
            for i in range(0, sides):
                ang = startAng + (i*centerAng);
                x = round(cx + radius*cos(ang), 2)
                y = round(cy - radius*sin(ang), 2)
                points.append([x,y])
            return points
        
        def _set_grid(self):
            """ Set up background grid for drawing canvas """
            grid_sizex = self.__dict__['size'][0]/self.__dict__['grid_aspects'][0]
            grid_sizey = self.__dict__['size'][1]/self.__dict__['grid_aspects'][0]
            
            sizex = grid_sizex*(1.0/self.__dict__['grid_aspects'][1])
            sizey = grid_sizey*(1.0/self.__dict__['grid_aspects'][1])
            
            defs = helper.defs()
            defs += helper.pattern(helper.path(**{'d': 'M %s 0 L 0 0 0 %s' % (sizex, sizey), 'fill': 'none', 'stroke': 'gray', 'stroke-width': 0.5}),
                                   **{'id': self.__dict__['grid_item_id'], 'width': sizex, 'height': sizey, 'patternUnits': 'userSpaceOnUse'})
            defs += helper.pattern(helper.path(**{'d': 'M %s 0 L 0 0 0 %s' % (grid_sizex, grid_sizey), 'fill': 'none', 'stroke': 'gray', 'stroke-width': 1}),
                                   helper.rect(width=grid_sizex, height=grid_sizey, fill="url(#%s)" % self.__dict__['grid_item_id']),
                                   **{'id': self.__dict__['grid_id'], 'width': grid_sizex, 'height': grid_sizey, 'patternUnits': 'userSpaceOnUse'})
            self += defs
            self += helper.rect(fill="white", height=self.__dict__['size'][0]+1, width=self.__dict__['size'][1]+1)
            self += helper.rect(fill="url(#%s)" % self.__dict__['grid_id'], height=self.__dict__['size'][0]+1, width=self.__dict__['size'][1]+1)
            
        def _set_axes(self):
            """ Set up x and y axis for drawing canvas """
            self += helper.line(stroke="black", x1=self.__dict__['x'], x2=self.__dict__['x'], y1=0, y2=self.__dict__['y']*2)
            self += helper.line(stroke="black", x1=0, x2=self.__dict__['x']*2, y1=self.__dict__['y'], y2=self.__dict__['y'])
        
        def _set_origin(self):
            """ Set up origin dot and x/y coordinates for drawing canvas """
            self += helper.circle(cx=self.__dict__['x'], cy=self.__dict__['y'], r=2, fill="black", stroke="black", style="fill-opacity: 50%")
            self += helper.text("(0,0)", x=self.__dict__['x']+5, y=self.__dict__['y']-5, style="fill-opacity: 50%")
        
        def __str__(self):
            if self.__dict__['grid']:
                self._set_grid()
            if self.__dict__['axes']:
                self._set_axes()
            if self.__dict__['origin']:
                self._set_origin()
            if self.__dict__['elements']:
                for element in self.__dict__['elements']:
                    self += element
            return super(self.__class__, self).__str__()
    
    return svg(*args, **kw)
736685
function DrawKey_graph (obj, key, colors)
        {
            var text_size   = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'];
            var text_italic = prop['chart.key.text.italic'] ?  true : false
            var text_bold   = prop['chart.key.text.bold'] ?  true : false
            var text_font   = prop['chart.key.text.font'] || prop['chart.key.font'] || prop['chart.text.font'];

            var gutterLeft   = obj.gutterLeft;
            var gutterRight  = obj.gutterRight;
            var gutterTop    = obj.gutterTop;
            var gutterBottom = obj.gutterBottom;
            var hpos         = prop['chart.yaxispos'] == 'right' ? gutterLeft + 10 : ca.width - gutterRight - 10;
            var vpos         = gutterTop + 10;
            var title        = prop['chart.title'];
            var blob_size    = text_size; // The blob of color
            var hmargin      = 8; // This is the size of the gaps between the blob of color and the text
            var vmargin      = 4; // This is the vertical margin of the key
            var fillstyle    = prop['chart.key.background'];
            var text_color   = prop['chart.key.text.color'];
            var strokestyle  = '#333';
            var height       = 0;
            var width        = 0;
    
            if (!obj.coords) obj.coords = {};
            obj.coords.key = [];
    
    
            // Need to set this so that measuring the text works out OK
            co.font = text_size + 'pt ' + prop['chart.text.font'];
    
            // Work out the longest bit of text
            for (i=0; i<key.length; ++i) {
                width = Math.max(width, co.measureText(key[i]).width);
            }
    
            width += 5;
            width += blob_size;
            width += 5;
            width += 5;
            width += 5;
    
            /**
            * Now we know the width, we can move the key left more accurately
            */
            if (   prop['chart.yaxispos'] == 'left'
                || (obj.type === 'pie' && !prop['chart.yaxispos'])
                || (obj.type === 'hbar' && !prop['chart.yaxispos'])
                || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'center')
                || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'right')
                || (obj.type === 'rscatter' && !prop['chart.yaxispos'])
                || (obj.type === 'radar' && !prop['chart.yaxispos'])
                || (obj.type === 'rose' && !prop['chart.yaxispos'])
                || (obj.type === 'funnel' && !prop['chart.yaxispos'])
                || (obj.type === 'vprogress' && !prop['chart.yaxispos'])
                || (obj.type === 'hprogress' && !prop['chart.yaxispos'])
               ) {

                hpos -= width;
            }

            /**
            * Horizontal alignment
            */
            if (typeof(prop['chart.key.halign']) == 'string') {
                if (prop['chart.key.halign'] == 'left') {
                    hpos = gutterLeft + 10;
                } else if (prop['chart.key.halign'] == 'right') {
                    hpos = ca.width - gutterRight  - width;
                }
            }
    
            /**
            * Specific location coordinates
            */
            if (typeof(prop['chart.key.position.x']) == 'number') {
                hpos = prop['chart.key.position.x'];
            }
            
            if (typeof(prop['chart.key.position.y']) == 'number') {
                vpos = prop['chart.key.position.y'];
            }
    
    
            // Stipulate the shadow for the key box
            if (prop['chart.key.shadow']) {
                co.shadowColor   = prop['chart.key.shadow.color'];
                co.shadowBlur    = prop['chart.key.shadow.blur'];
                co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
                co.shadowOffsetY = prop['chart.key.shadow.offsety'];
            }
    
    
    
    
            // Draw the box that the key resides in
            co.beginPath();
                co.fillStyle   = prop['chart.key.background'];
                co.strokeStyle = 'black';
    
            if (typeof(prop['chart.key.position.graph.boxed']) == 'undefined' || (typeof(prop['chart.key.position.graph.boxed']) == 'boolean' && prop['chart.key.position.graph.boxed']) ) {
                if (arguments[3] != false) {
        
                    co.lineWidth = typeof(prop['chart.key.linewidth']) == 'number' ? prop['chart.key.linewidth'] : 1;
    
                    // The older square rectangled key
                    if (prop['chart.key.rounded'] == true) {
                        co.beginPath();
                            co.strokeStyle = strokestyle;
                            RG.strokedCurvyRect(co, Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)),4);
                        co.stroke();
                        co.fill();
    
                        RG.NoShadow(obj);
                
                    } else {
                        co.strokeRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
                        co.fillRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
                    }
                }
            }
    
            RG.NoShadow(obj);
    
            co.beginPath();
    
                /**
                * Custom colors for the key
                */
                if (prop['chart.key.colors']) {
                    colors = prop['chart.key.colors'];
                }
    
    
    
                ////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
                // Draw the labels given
                for (var i=key.length - 1; i>=0; i--) {
                
                    var j = Number(i) + 1;
    
                    /**
                    * Draw the blob of color
                    */
                    if (typeof(prop['chart.key.color.shape']) == 'object' && typeof(prop['chart.key.color.shape'][i]) == 'string') {
                        var blob_shape = prop['chart.key.color.shape'][i];
                    
                    } else if (typeof(prop['chart.key.color.shape']) == 'string') {
                        var blob_shape = prop['chart.key.color.shape'];
                    } else {
                        var blob_shape = 'square';
                    }
                    
                    if (blob_shape == 'circle') {
                        co.beginPath();
                            co.fillStyle = colors[i];
                            co.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0);
                        co.fill();
                    
                    } else if (blob_shape == 'line') {
                        co.beginPath();
                            co.strokeStyle = colors[i];
                            co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
                            co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
                        co.stroke();
                    
                    } else if (blob_shape == 'triangle') {
                        co.beginPath();
                            co.strokeStyle = colors[i];
                            co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
                            co.lineTo(hpos + (blob_size / 2) + 5, vpos + (5 * j) + (text_size * j) - text_size );
                            co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
                        co.closePath();
                        co.fillStyle =  colors[i];
                        co.fill();
                    
                    } else {
                        co.fillStyle =  colors[i];
                        co.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1);
                    }
                    
                    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
                    co.beginPath();
                    co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;
                
                    ret = RG.Text2(obj, {'font': text_font,
                                         'size': text_size,
                                         'bold': text_bold,
                                         'italic': text_italic,
                                         'x': hpos + blob_size + 5 + 5,
                                         'y': vpos + (5 * j) + (text_size * j) + 3,
                                         'text': key[i]});

                    obj.coords.key[i] = [ret.x, ret.y, ret.width, ret.height, key[i], colors[i], obj];
                }
            co.fill();
        }
54719
function (str, x, y) {
		var defaultChartStyle = defaultOptions.chart.style,
			wrapper = this.createElement('span'),
			attrSetters = wrapper.attrSetters,
			element = wrapper.element,
			renderer = wrapper.renderer;

		// Text setter
		attrSetters.text = function (value) {
			if (value !== element.innerHTML) {
				delete this.bBox;
			}
			element.innerHTML = value;
			return false;
		};

		// Various setters which rely on update transform
		attrSetters.x = attrSetters.y = attrSetters.align = function (value, key) {
			if (key === 'align') {
				key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML.
			}
			wrapper[key] = value;
			wrapper.htmlUpdateTransform();
			return false;
		};

		// Set the default attributes
		wrapper.attr({
				text: str,
				x: mathRound(x),
				y: mathRound(y)
			})
			.css({
				position: ABSOLUTE,
				whiteSpace: 'nowrap',
				fontFamily: defaultChartStyle.fontFamily,
				fontSize: defaultChartStyle.fontSize
			});

		// Use the HTML specific .css method
		wrapper.css = wrapper.htmlCss;

		// This is specific for HTML within SVG
		if (renderer.isSVG) {
			wrapper.add = function (svgGroupWrapper) {

				var htmlGroup,
					container = renderer.box.parentNode,
					parentGroup,
					parents = [];

				// Create a mock group to hold the HTML elements
				if (svgGroupWrapper) {
					htmlGroup = svgGroupWrapper.div;
					if (!htmlGroup) {

						// Read the parent chain into an array and read from top down
						parentGroup = svgGroupWrapper;
						while (parentGroup) {

							parents.push(parentGroup);

							// Move up to the next parent group
							parentGroup = parentGroup.parentGroup;
						}

						// Ensure dynamically updating position when any parent is translated
						each(parents.reverse(), function (parentGroup) {
							var htmlGroupStyle;

							// Create a HTML div and append it to the parent div to emulate
							// the SVG group structure
							htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, {
								className: attr(parentGroup.element, 'class')
							}, {
								position: ABSOLUTE,
								left: (parentGroup.translateX || 0) + PX,
								top: (parentGroup.translateY || 0) + PX
							}, htmlGroup || container); // the top group is appended to container

							// Shortcut
							htmlGroupStyle = htmlGroup.style;

							// Set listeners to update the HTML div's position whenever the SVG group
							// position is changed
							extend(parentGroup.attrSetters, {
								translateX: function (value) {
									htmlGroupStyle.left = value + PX;
								},
								translateY: function (value) {
									htmlGroupStyle.top = value + PX;
								},
								visibility: function (value, key) {
									htmlGroupStyle[key] = value;
								}
							});
						});

					}
				} else {
					htmlGroup = container;
				}

				htmlGroup.appendChild(element);

				// Shared with VML:
				wrapper.added = true;
				if (wrapper.alignOnAdd) {
					wrapper.htmlUpdateTransform();
				}

				return wrapper;
			};
		}
		return wrapper;
	}