docs: function () {
  		var series = this,
  			data = series.data,
  			point,
  			chart = series.chart,
  			options = series.options.dataLabels,
  			connectorPadding = pick(options.connectorPadding, 10),
  			connectorWidth = pick(options.connectorWidth, 1),
  			connector,
  			connectorPath,
  			softConnector = pick(options.softConnector, true),
  			distanceOption = options.distance,
  			seriesCenter = series.center,
  			radius = seriesCenter[2] / 2,
  			centerY = seriesCenter[1],
  			outside = distanceOption > 0,
  			dataLabel,
  			labelPos,
  			labelHeight,
  			halves = [// divide the points into right and left halves for anti collision
  				[], // right
  				[]  // left
  			],
  			x,
  			y,
  			visibility,
  			rankArr,
  			sort,
  			i = 2,
  			j;
  
  		// get out if not enabled
  		if (!options.enabled) {
  			return;
  		}
  
  		// run parent method
  		Series.prototype.drawDataLabels.apply(series);
  
  		// arrange points for detection collision
  		each(data, function (point) {
  			if (point.dataLabel) { // it may have been cancelled in the base method (#407)
  				halves[
  					point.labelPos[7] < mathPI / 2 ? 0 : 1
  				].push(point);
  			}
  		});
  		halves[1].reverse();
  
  		// define the sorting algorithm
  		sort = function (a, b) {
  			return b.y - a.y;
  		};
  
  		// assume equal label heights
  		labelHeight = halves[0][0] && halves[0][0].dataLabel && pInt(halves[0][0].dataLabel.styles.lineHeight);
  
  		/* Loop over the points in each quartile, starting from the top and bottom
  		 * of the pie to detect overlapping labels.
  		 */
  		while (i--) {
  
  			var slots = [],
  				slotsLength,
  				usedSlots = [],
  				points = halves[i],
  				pos,
  				length = points.length,
  				slotIndex;
  
  
  			// build the slots
  			for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
  				slots.push(pos);
  				// visualize the slot
  				/*
  				var //slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
  				    slotX = chart.plotLeft + series.center[0] + (i ? (radius + distanceOption) : (-radius - distanceOption - 100)),
  					slotY = pos + chart.plotTop;
  				if (!isNaN(slotX)) {
  					chart.renderer.rect(slotX, slotY - 7, 100, labelHeight)
  						.attr({
  							'stroke-width': 1,
  							stroke: 'silver'
  						})
  						.add();
  					chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
  						.attr({
  							fill: 'silver'
  						}).add();
  				}
  				// */
  			}
  			slotsLength = slots.length;
  
  			// if there are more values than available slots, remove lowest values
  			if (length > slotsLength) {
  				// create an array for sorting and ranking the points within each quarter
  				rankArr = [].concat(points);
  				rankArr.sort(sort);
  				j = length;
  				while (j--) {
  					rankArr[j].rank = j;
  				}
  				j = length;
  				while (j--) {
  					if (points[j].rank >= slotsLength) {
  						points.splice(j, 1);
  					}
  				}
  				length = points.length;
  			}
  
  			// The label goes to the nearest open slot, but not closer to the edge than
  			// the label's index.
  			for (j = 0; j < length; j++) {
  
  				point = points[j];
  				labelPos = point.labelPos;
  
  				var closest = 9999,
  					distance,
  					slotI;
  
  				// find the closest slot index
  				for (slotI = 0; slotI < slotsLength; slotI++) {
  					distance = mathAbs(slots[slotI] - labelPos[1]);
  					if (distance < closest) {
  						closest = distance;
  						slotIndex = slotI;
  					}
  				}
  
  				// if that slot index is closer to the edges of the slots, move it
  				// to the closest appropriate slot
  				if (slotIndex < j && slots[j] !== null) { // cluster at the top
  					slotIndex = j;
  				} else if (slotsLength  < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
  					slotIndex = slotsLength - length + j;
  					while (slots[slotIndex] === null) { // make sure it is not taken
  						slotIndex++;
  					}
  				} else {
  					// Slot is taken, find next free slot below. In the next run, the next slice will find the
  					// slot above these, because it is the closest one
  					while (slots[slotIndex] === null) { // make sure it is not taken
  						slotIndex++;
  					}
  				}
  
  				usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
  				slots[slotIndex] = null; // mark as taken
  			}
  			// sort them in order to fill in from the top
  			usedSlots.sort(sort);
  
  
  			// now the used slots are sorted, fill them up sequentially
  			for (j = 0; j < length; j++) {
  
  				point = points[j];
  				labelPos = point.labelPos;
  				dataLabel = point.dataLabel;
  				var slot = usedSlots.pop(),
  					naturalY = labelPos[1];
  
  				visibility = point.visible === false ? HIDDEN : VISIBLE;
  				slotIndex = slot.i;
  
  				// if the slot next to currrent slot is free, the y value is allowed
  				// to fall back to the natural position
  				y = slot.y;
  				if ((naturalY > y && slots[slotIndex + 1] !== null) ||
  						(naturalY < y &&  slots[slotIndex - 1] !== null)) {
  					y = naturalY;
  				}
  
  				// get the x - use the natural x position for first and last slot, to prevent the top
  				// and botton slice connectors from touching each other on either side
                  
                  // PATCH by Simon Fishel
                  //
                  // execute xPositionHook to override the x positioning of data labels
                  //
                  // - x = series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
                  //
                  // + if(options.hooks && options.hooks.xPositionHook) {
                  // +     x = options.hooks.xPositionHook(series, options, radiusY, (i != 0));
                  // + }
                  // + else {
                  // +     x = series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
                  // + }
                  
                  if(options.hooks && options.hooks.xPositionHook) {
                      x = options.hooks.xPositionHook(series, options, radius, (i == 0));
                  }
                  else {
                      x = series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
                  }
  
  				// move or place the data label
  				dataLabel
  					.attr({
  						visibility: visibility,
  						align: labelPos[6]
  					})[dataLabel.moved ? 'animate' : 'attr']({
  						x: x + options.x +
  							({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0),
  						y: y + options.y
  					});
  				dataLabel.moved = true;
  
  				// draw the connector
  				if (outside && connectorWidth) {
  					connector = point.connector;
  
  					connectorPath = softConnector ? [
  						M,
  						x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
  						'C',
  						x, y, // first break, next to the label
  						2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
  						labelPos[2], labelPos[3], // second break
  						L,
  						labelPos[4], labelPos[5] // base
  					] : [
  						M,
  						x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
  						L,
  						labelPos[2], labelPos[3], // second break
  						L,
  						labelPos[4], labelPos[5] // base
  					];
  						
  					// PATCH by Simon Fishel
                      //
                      // call the connector position hook to allow modifications to the connector line path
                      //
                      // + if(!softConnector && options.hooks && options.hooks.connectorPositionHook) {
                      // +     options.hooks.connectorPositionHook(connectorPath);
                      // + }
                      
                      if(!softConnector && options.hooks && options.hooks.connectorPositionHook) {
                          options.hooks.connectorPositionHook(connectorPath);
                      }
  
  					if (connector) {
  						connector.animate({ d: connectorPath });
  						connector.attr('visibility', visibility);
  
  					} else {
  						point.connector = connector = series.chart.renderer.path(connectorPath).attr({
  							'stroke-width': connectorWidth,
  							stroke: options.connectorColor || point.color || '#606060',
  							visibility: visibility,
  							zIndex: 3
  						})
  						.translate(chart.plotLeft, chart.plotTop)
  						.add();
  					}
  				}
  			}
  		}
  	}