docs: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  
              var renderer = this,
                wrapper = renderer.g(className),
                text = renderer.text('', 0, 0, useHTML)
                  .attr({
                      zIndex: 1
                  }),
              //.add(wrapper),
                box,
                bBox,
                alignFactor = 0,
                padding = 3,
                paddingLeft = 0,
                width,
                height,
                wrapperX,
                wrapperY,
                crispAdjust = 0,
                deferredAttr = {},
                baselineOffset,
                needsBox;
  
              /**
               * This function runs after the label is added to the DOM (when the bounding box is
               * available), and after the text of the label is updated to detect the new bounding
               * box and reflect it in the border box.
               */
              function updateBoxSize() {
                  var boxX,
                    boxY,
                    style = text.element.style;
  
                  bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) && text.textStr &&
                    text.getBBox();
                  wrapper.width = (width || bBox.width || 0) + 2 * padding + paddingLeft;
                  wrapper.height = (height || bBox.height || 0) + 2 * padding;
  
                  // update the label-scoped y offset
                  baselineOffset = padding + renderer.fontMetrics(style && style.fontSize, text).b;
  
  
                  if (needsBox) {
  
                      // create the border box if it is not already present
                      if (!box) {
                          boxX = mathRound(-alignFactor * padding);
                          boxY = baseline ? -baselineOffset : 0;
  
                          wrapper.box = box = shape ?
                            renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height, deferredAttr) :
                            renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
                          box.attr('fill', NONE).add(wrapper);
                      }
  
                      // apply the box attributes
                      if (!box.isImg) { // #1630
                          box.attr(extend({
                              width: mathRound(wrapper.width),
                              height: mathRound(wrapper.height)
                          }, deferredAttr));
                      }
                      deferredAttr = null;
                  }
              }
  
              /**
               * This function runs after setting text or padding, but only if padding is changed
               */
              function updateTextPadding() {
                  var styles = wrapper.styles,
                    textAlign = styles && styles.textAlign,
                    x = paddingLeft + padding * (1 - alignFactor),
                    y;
  
                  // determin y based on the baseline
                  y = baseline ? 0 : baselineOffset;
  
                  // compensate for alignment
                  if (defined(width) && bBox && (textAlign === 'center' || textAlign === 'right')) {
                      x += { center: 0.5, right: 1 }[textAlign] * (width - bBox.width);
                  }
  
                  // update if anything changed
                  if (x !== text.x || y !== text.y) {
                      text.attr('x', x);
                      if (y !== UNDEFINED) {
                          text.attr('y', y);
                      }
                  }
  
                  // record current values
                  text.x = x;
                  text.y = y;
              }
  
              /**
               * Set a box attribute, or defer it if the box is not yet created
               * @param {Object} key
               * @param {Object} value
               */
              function boxAttr(key, value) {
                  if (box) {
                      box.attr(key, value);
                  } else {
                      deferredAttr[key] = value;
                  }
              }
  
              /**
               * After the text element is added, get the desired size of the border box
               * and add it before the text in the DOM.
               */
              wrapper.onAdd = function () {
                  text.add(wrapper);
                  wrapper.attr({
                      text: (str || str === 0) ? str : '', // alignment is available now // #3295: 0 not rendered if given as a value
                      x: x,
                      y: y
                  });
  
                  if (box && defined(anchorX)) {
                      wrapper.attr({
                          anchorX: anchorX,
                          anchorY: anchorY
                      });
                  }
              };
  
              /*
               * Add specific attribute setters.
               */
  
              // only change local variables
              wrapper.widthSetter = function (value) {
                  width = value;
              };
              wrapper.heightSetter = function (value) {
                  height = value;
              };
              wrapper.paddingSetter =  function (value) {
                  if (defined(value) && value !== padding) {
                      padding = value;
                      updateTextPadding();
                  }
              };
              wrapper.paddingLeftSetter =  function (value) {
                  if (defined(value) && value !== paddingLeft) {
                      paddingLeft = value;
                      updateTextPadding();
                  }
              };
  
  
              // change local variable and prevent setting attribute on the group
              wrapper.alignSetter = function (value) {
                  alignFactor = { left: 0, center: 0.5, right: 1 }[value];
              };
  
              // apply these to the box and the text alike
              wrapper.textSetter = function (value) {
                  if (value !== UNDEFINED) {
                      text.textSetter(value);
                  }
                  updateBoxSize();
                  updateTextPadding();
              };
  
              // apply these to the box but not to the text
              wrapper['stroke-widthSetter'] = function (value, key) {
                  if (value) {
                      needsBox = true;
                  }
                  crispAdjust = value % 2 / 2;
                  boxAttr(key, value);
              };
              wrapper.strokeSetter = wrapper.fillSetter = wrapper.rSetter = function (value, key) {
                  if (key === 'fill' && value) {
                      needsBox = true;
                  }
                  boxAttr(key, value);
              };
              wrapper.anchorXSetter = function (value, key) {
                  anchorX = value;
                  boxAttr(key, value + crispAdjust - wrapperX);
              };
              wrapper.anchorYSetter = function (value, key) {
                  anchorY = value;
                  boxAttr(key, value - wrapperY);
              };
  
              // rename attributes
              wrapper.xSetter = function (value) {
                  wrapper.x = value; // for animation getter
                  if (alignFactor) {
                      value -= alignFactor * ((width || bBox.width) + padding);
                  }
                  wrapperX = mathRound(value);
                  wrapper.attr('translateX', wrapperX);
              };
              wrapper.ySetter = function (value) {
                  wrapperY = wrapper.y = mathRound(value);
                  wrapper.attr('translateY', wrapperY);
              };
  
              // Redirect certain methods to either the box or the text
              var baseCss = wrapper.css;
              return extend(wrapper, {
                  /**
                   * Pick up some properties and apply them to the text instead of the wrapper
                   */
                  css: function (styles) {
                      if (styles) {
                          var textStyles = {};
                          styles = merge(styles); // create a copy to avoid altering the original object (#537)
                          each(wrapper.textProps, function (prop) {
                              if (styles[prop] !== UNDEFINED) {
                                  textStyles[prop] = styles[prop];
                                  delete styles[prop];
                              }
                          });
                          text.css(textStyles);
                      }
                      return baseCss.call(wrapper, styles);
                  },
                  /**
                   * Return the bounding box of the box, not the group
                   */
                  getBBox: function () {
                      return {
                          width: bBox.width + 2 * padding,
                          height: bBox.height + 2 * padding,
                          x: bBox.x - padding,
                          y: bBox.y - padding
                      };
                  },
                  /**
                   * Apply the shadow to the box
                   */
                  shadow: function (b) {
                      if (box) {
                          box.shadow(b);
                      }
                      return wrapper;
                  },
                  /**
                   * Destroy and release memory.
                   */
                  destroy: function () {
  
                      // Added by button implementation
                      removeEvent(wrapper.element, 'mouseenter');
                      removeEvent(wrapper.element, 'mouseleave');
  
                      if (text) {
                          text = text.destroy();
                      }
                      if (box) {
                          box = box.destroy();
                      }
                      // Call base implementation to destroy the rest
                      SVGElement.prototype.destroy.call(wrapper);
  
                      // Release local pointers (#1298)
                      wrapper = renderer = updateBoxSize = updateTextPadding = boxAttr = null;
                  }
              });
          }