/* global $ Highcharts document window module:true */

(function (factory) {

	if (typeof module === 'object' && module.exports) {

		module.exports = factory;

	} else {

		factory(Highcharts);

	}

}(function (H) {

	'use strict';

	// Highcharts helper methods

	var UNDEFINED,

		ALIGN_FACTOR,

		Chart = H.Chart,

		extend = H.extend,

		merge = H.merge,

		each = H.each,

		inArray = (window.HighchartsAdapter && window.HighchartsAdapter.inArray) || H.inArray, // #52, since Highcharts 4.1.10 HighchartsAdapter is only provided by the Highcharts Standalone Framework

		addEvent = H.addEvent,

		removeEvent = H.removeEvent,

		isOldIE = H.VMLRenderer ? true : false;



	H.ALLOWED_SHAPES = ['path', 'rect', 'circle'];



	ALIGN_FACTOR = {

		top: 0,

		left: 0,

		center: 0.5,

		middle: 0.5,

		bottom: 1,

		right: 1

	};





	H.SVGRenderer.prototype.symbols.line = function (x, y, w, h) {

		var p = 2;

		return [

			'M', x + p, y + p, 'L', x + w - p, y + h - p

		];

	};



	H.SVGRenderer.prototype.symbols.text = function (x, y, w, h) {

		var p = 1;

		return [

			// 'M', 0, 0, 'L', 10, 0, 'M', 5, 0, 'L', 5, 5

			'M', x, y + p, 'L', x + w, y + p,

			'M', x + w / 2, y + p, 'L', x + w / 2, y + p + h

		];

	};

	// VML fallback

	if (H.VMLRenderer) {

		H.VMLRenderer.prototype.symbols.text = H.SVGRenderer.prototype.symbols.text;

		H.VMLRenderer.prototype.symbols.line = H.SVGRenderer.prototype.symbols.line;

	}





	// when drawing annotation, don't zoom/select place

	H.wrap(H.Pointer.prototype, 'drag', function (c, e) {

		if (!this.chart.annotations || this.chart.annotations.allowZoom) {

			c.call(this, e);

		}

	});



	// deselect active annotation

	H.wrap(H.Pointer.prototype, 'onContainerMouseDown', function (c, e) {

		c.call(this, e);

		if (this.chart.selectedAnnotation) {

			this.chart.selectedAnnotation.events.deselect.call(this.chart.selectedAnnotation, e);

		}

	});



	// utils for buttons

	var utils = {

		getRadius: function (e) {

			var ann = this,

				chart = ann.chart,

				bbox = chart.container.getBoundingClientRect(),

				x = e.clientX - bbox.left,

				y = e.clientY - bbox.top,

				xAxis = chart.xAxis[ann.options.xAxis],

				yAxis = chart.yAxis[ann.options.yAxis],

				dx = Math.abs(x - xAxis.toPixels(ann.options.xValue)),

				dy = Math.abs(y - yAxis.toPixels(ann.options.yValue)),

				radius = parseInt(Math.sqrt(dx * dx + dy * dy), 10);

			ann.shape.attr({

				r: radius

			});

			return radius;

		},

		getRadiusAndUpdate:	function (e) {

			var r = utils.getRadius.call(this, e);

			this.update({

				shape: {

					params: {

						r: r,

						x: -r,

						y: -r

					}

				}

			});

		},

		getPath: function (e) {

			var ann = this,

				chart = ann.chart,

				bbox = chart.container.getBoundingClientRect(),

				x = e.clientX - bbox.left,

				y = e.clientY - bbox.top,

				xAxis = chart.xAxis[ann.options.xAxis],

				yAxis = chart.yAxis[ann.options.yAxis],

				dx = x - xAxis.toPixels(ann.options.xValue),

				dy = y - yAxis.toPixels(ann.options.yValue);

				

			var path = ['M', 0, 0, 'L', parseInt(dx, 10), parseInt(dy, 10)];

			ann.shape.attr({

				d: path

			});

				

			return path;

		},

		getPathAndUpdate: function (e) {

			var ann = this,

				chart = ann.chart,

				path = utils.getPath.call(ann, e),

				xAxis = chart.xAxis[ann.options.xAxis],

				yAxis = chart.yAxis[ann.options.yAxis],

				x = xAxis.toValue(path[4] + xAxis.toPixels(ann.options.xValue)),

				y = yAxis.toValue(path[5] + yAxis.toPixels(ann.options.yValue));

				

			this.update({

				xValueEnd: x,

				yValueEnd: y,

				shape: {

					params: {

						d: path

					}

				}

			});

		},

		getRect: function (e) {

			var ann = this,

				chart = ann.chart,

				bbox = chart.container.getBoundingClientRect(),

				x = e.clientX - bbox.left,

				y = e.clientY - bbox.top,

				xAxis = chart.xAxis[ann.options.xAxis],

				yAxis = chart.yAxis[ann.options.yAxis],

				sx = xAxis.toPixels(ann.options.xValue),

				sy = yAxis.toPixels(ann.options.yValue),

				dx = x - sx,

				dy = y - sy,

				w = Math.round(dx) + 1,

				h = Math.round(dy) + 1,

				ret = {};

				

			ret.x = w < 0 ? w : 0;

			ret.width = Math.abs(w);

			ret.y = h < 0 ? h : 0;

			ret.height = Math.abs(h);

						

			ann.shape.attr({

				x: ret.x,

				y: ret.y,

				width: ret.width,

				height: ret.height

			});

			return ret;

		},

		getRectAndUpdate: function (e) {

			var rect = utils.getRect.call(this, e);

			this.update({

				shape: {

					params: rect

				}

			});

		},

		getText: function () {

			// do nothing

		},

		showInput: function (e) {

			var ann = this,

				chart = ann.chart,

				index = chart.annotationInputIndex = chart.annotationInputIndex ? chart.annotationInputIndex : 1,

				input = document.createElement('span'),

				button;

					

			input.innerHTML = '<input type="text" class="annotation-' + index + '" placeholder="Add text"><button class=""> Done </button>';

			input.style.position = 'absolute';

			input.style.left = e.pageX + 'px';

			input.style.top = e.pageY + 'px';

			

			document.body.appendChild(input);

			input.querySelectorAll('input')[0].focus();

			button = input.querySelectorAll('button')[0];

			button.onclick = function () {

				var parent = this.parentNode;

				

				ann.update({

					title: {

						text: parent.querySelectorAll('input')[0].value

					}

				});

				parent.parentNode.removeChild(parent);

			};

			chart.annotationInputIndex++;

		}

	};

			

	function defaultOptions(shapeType) {

		var shapeOptions,

			options;



		options = {

			xAxis: 0,

			yAxis: 0,

			shape: {

				params: {

					stroke: '#000000',

					fill: 'rgba(0,0,0,0)',

					'stroke-width': 2

				}

			},

			selectionMarker: {

				'stroke-width': 1,

				stroke: 'black',

				fill: 'transparent',

				dashstyle: 'ShortDash',

				'shape-rendering': 'crispEdges'

			}

		};



		shapeOptions = {

			circle: {

				params: {

					x: 0,

					y: 0

				}

			}

		};



		if (shapeOptions[shapeType]) {

			options.shape = merge(options.shape, shapeOptions[shapeType]);

		}



		return options;

	}





	function defatultMainOptions() {

		var buttons = [],

			shapes = ['circle', 'line', 'square', 'text'],

			types = ['circle', 'path', 'rect', null],

			params = [{

				r: 0,

				fill: 'rgba(255,0,0,0.4)',

				stroke: 'black'

			}, {

				d: ['M', 0, 0, 'L', 10, 10],

				fill: 'rgba(255,0,0,0.4)',

				stroke: 'black'

			}, {

				width: 10,

				height: 10,

				fill: 'rgba(255,0,0,0.4)',

				stroke: 'black'

			}],

			steps = [utils.getRadius, utils.getPath, utils.getRect, utils.getText],

			stops = [utils.getRadiusAndUpdate, utils.getPathAndUpdate, utils.getRectAndUpdate, utils.showInput];

			

		each(shapes, function (s, i) {

			buttons.push({

				annotationEvents: {

					step: steps[i],

					stop: stops[i]

				},

				annotation: {

					anchorX: 'left',

					anchorY: 'top',

					xAxis: 0,

					yAxis: 0,

					shape: {

						type: types[i],

						params: params[i]

					}

				},

				symbol: {

					shape: s,

					size: 12,

					style: {

						'stroke-width': 2,

						'stroke': 'black',

						fill: 'red',

						zIndex: 121

					}

				},

				style: {

					fill: 'black',

					stroke: 'blue',

					strokeWidth: 2

				},

				size: 12,

				states: {

					selected: {

						fill: '#9BD'

					},

					hover: {

						fill: '#9BD'

					}

				}

			});

		});

			

		return {

			enabledButtons: true,

			buttons: buttons,

			buttonsOffsets: [0, 0]

		};

	}



	function isArray(obj) {

		return Object.prototype.toString.call(obj) === '[object Array]';

	}



	function isNumber(n) {

		return typeof n === 'number';

	}



	function defined(obj) {

		return obj !== UNDEFINED && obj !== null;

	}



	function translatePath(d, xAxis, yAxis, xOffset, yOffset) {

		var len = d.length,

			i = 0,

			path = [];



		while (i < len) {

			if (typeof d[i] === 'number' && typeof d[i + 1] === 'number') {

				path[i] = xAxis.toPixels(d[i]) - xOffset;

				path[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset;

				i += 2;

			} else {

				path[i] = d[i];

				i += 1;

			}

		}



		return path;

	}



	function createGroup(chart, i, clipPath) {

		var group = chart.renderer.g('annotations-group-' + i);

		group.attr({

			zIndex: 7

		});

		group.add();

		group.clip(clipPath);

		return group;

	}



	function createClipPath(chart, y) {

		var clipBox = {

			x: y.left,

			y: y.top,

			width: y.width,

			height: y.height

		};

				

		return chart.renderer.clipRect(clipBox);

	}



	function attachEvents(chart) {

		function step(e) {

			var selected = chart.annotations.selected;

			chart.annotations.options.buttons[selected].annotationEvents.step.call(chart.drawAnnotation, e);

		}

		function drag(e) {

			var bbox = chart.container.getBoundingClientRect(),

				clickX = e.clientX - bbox.left,

				clickY = e.clientY - bbox.top;

			

			if (!chart.isInsidePlot(clickX - chart.plotLeft, clickY - chart.plotTop) || chart.annotations.allowZoom) {

				return;

			}

			

			var xAxis = chart.xAxis[0],

				yAxis = chart.yAxis[0],

				selected = chart.annotations.selected;

			

			var options = merge(chart.annotations.options.buttons[selected].annotation, {

				xValue: xAxis.toValue(clickX),

				yValue: yAxis.toValue(clickY),

				allowDragX: true,

				allowDragY: true

			});

			

			chart.addAnnotation(options);

			

			chart.drawAnnotation = chart.annotations.allItems[chart.annotations.allItems.length - 1];

			addEvent(document, 'mousemove', step);

		}

		

		function drop(e) {

			removeEvent(document, 'mousemove', step);

			

			// store annotation details

			if (chart.drawAnnotation) {

				var selected = chart.annotations.selected;

				chart.annotations.options.buttons[selected].annotationEvents.stop.call(chart.drawAnnotation, e);

			}

			chart.drawAnnotation = null;

		}

		addEvent(chart.container, 'mousedown', drag);

		addEvent(document, 'mouseup', drop);

	}



	function getButtonCallback(index, chart) {

		return function () {

			var self = chart.annotations.buttons[index][0];

			if (self.state === 2) {

				chart.annotations.selected = -1;

				chart.annotations.allowZoom = true;

				self.setState(0);

			} else {

				if (chart.annotations.selected >= 0) {

					chart.annotations.buttons[chart.annotations.selected][0].setState(0);

				}

				chart.annotations.allowZoom = false;

				chart.annotations.selected = index;

				self.setState(2);

			}

		};

	}





	function renderButton(chart, mainButton, i) {

		var userOffset = chart.annotations.options.buttonsOffsets,

			xOffset = chart.rangeSelector && chart.rangeSelector.inputGroup ? chart.rangeSelector.inputGroup.offset : 0,

			renderer = chart.renderer,

			symbol = mainButton.symbol,

			offset = 30,

			symbolSize = symbol.size,

			padding = 8 / 2, // since Highcahrts 5.0, padding = 8 is hardcoded

			buttonSize = mainButton.size - padding,

			x = chart.plotWidth + chart.plotLeft - ((i + 1) * offset) - xOffset - userOffset[0],

			y = chart.plotTop - (chart.rangeSelector ? 23 + buttonSize + padding : 0) + userOffset[1],

			callback = mainButton.events && mainButton.events.click ? mainButton.events.click : getButtonCallback(i, chart),

			selected = mainButton.states.selected,

			hovered = mainButton.states.hover,

			button,

			s;

			

		button = renderer.button('', x, y, callback, {}, hovered, selected).attr({ width: buttonSize, height: buttonSize, zIndex: 10 });

		s = renderer.symbol(

			symbol.shape,

			buttonSize - symbolSize / 2 + padding,

			buttonSize - symbolSize / 2 + padding,

			symbolSize,

			symbolSize

		).attr(symbol.style).add(button);

		

		button.attr(button.style).add();

		

		return [button, s];

	}



	function renderButtons(chart) {

		var buttons = chart.annotations.options.buttons;

		

		chart.annotations.buttons = chart.annotations.buttons || [];

		each(buttons, function (button, i) {

			chart.annotations.buttons.push(renderButton(chart, button, i));

		});

	}



	// Define annotation prototype

	var Annotation = function () { // eslint-disable-line

		this.init.apply(this, arguments);

	};



	Annotation.prototype = {

		/*

		 * Initialize the annotation

		 */

		init: function (chart, options) {

			var shapeType = options.shape && options.shape.type;



			this.chart = chart;

			this.options = merge({}, defaultOptions(shapeType), options);

			this.userOptions = options;

		},



		/*

		 * Render the annotation

		 */

		render: function (redraw) {

			var annotation = this,

				chart = this.chart,

				renderer = annotation.chart.renderer,

				group = annotation.group,

				title = annotation.title,

				shape = annotation.shape,

				options = annotation.options,

				titleOptions = options.title,

				shapeOptions = options.shape,

				allowDragX = options.allowDragX,

				allowDragY = options.allowDragY,

				hasEvents = annotation.hasEvents;



			function attachCustomEvents(element, events) {

				if (defined(events)) {

					for (var name in events) { // eslint-disable-line

						(function (n) {

							addEvent(element.element, n, function (e) {

								events[n].call(annotation, e);

							});

						})(name);

					}

				}

			}



			if (!group) {

				group = annotation.group = renderer.g();

				group.attr({ 'class': 'highcharts-annotation' });

			}



			if (!shape && shapeOptions && inArray(shapeOptions.type, H.ALLOWED_SHAPES) !== -1) {

				shape = annotation.shape = shapeOptions.type === 'rect' ? renderer[options.shape.type]().attr(shapeOptions.params) : renderer[options.shape.type](shapeOptions.params);

				shape.add(group);

			}



			if (!title && titleOptions) {

				title = annotation.title = renderer.label(titleOptions);

				title.add(group);

			}

			if ((allowDragX || allowDragY) && !hasEvents) {

				$(group.element).on('mousedown', function (e) {

					annotation.events.storeAnnotation(e, annotation, chart);

					annotation.events.select(e, annotation);

				});

				addEvent(document, 'mouseup', function (e) {

					annotation.events.releaseAnnotation(e, chart);

				});

				

				attachCustomEvents(group, options.events);

			} else if (!hasEvents) {

				$(group.element).on('mousedown', function (e) {

					annotation.events.select(e, annotation);

				});

				attachCustomEvents(group, options.events);

			}



			this.hasEvents = true;

			

			group.add(chart.annotations.groups[options.yAxis]);

			

			// link annotations to point or series

			annotation.linkObjects();

			

			if (redraw !== false) {

				annotation.redraw();

			}

		},



		/*

		 * Redraw the annotation title or shape after options update

		 */

		redraw: function (redraw) {

			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],

				shapeParams,

				linkType,

				series,

				bbox,

				x,

				y;



			if (linkedTo) {

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



				if (linkType === 'point') {

					options.x = linkedTo.plotX + chart.plotLeft;

					options.y = linkedTo.plotY + chart.plotTop;

					series = linkedTo.series;

				} else if (linkType === 'series') {

					series = linkedTo;

				}



				// #48 - series.grouping and series.stacking may reposition point.graphic

				if (series.pointXOffset) {

					options.x += series.pointXOffset + (linkedTo.shapeArgs.width / 2 || 0);

				}

				group.attr({

					visibility: series.group.attr('visibility')

				});

			}





			// Based on given options find annotation pixel position

			// what is minPointOffset? Doesn't work in 4.0+

			x = (defined(options.xValue) ? xAxis.toPixels(options.xValue /* + xAxis.minPointOffset */) : options.x);

			y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y;

			if (chart.inverted && defined(options.xValue) && defined(options.yValue)) {

				var tmp = x; x = y; y = tmp;

			}



			if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) {

				return;

			}





			if (title) {

				var attrs = options.title;

				if (isOldIE) {

					title.attr({

						text: attrs.text

					});

				} else {

					title.attr(attrs);

				}

				title.css(options.title.style);

			}



			if (shape) {

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

				if (options.shape.units === 'values') {

					var realXAxis = chart.inverted ? yAxis : xAxis;

					var realYAxis = chart.inverted ? xAxis : yAxis;



					// For ordinal axis, required are x&Y values - #22

					if (defined(shapeParams.x) && shapeParams.width) {

						shapeParams.width = xAxis.toPixels(shapeParams.width + shapeParams.x) - xAxis.toPixels(shapeParams.x);

						shapeParams.x = xAxis.toPixels(shapeParams.x);

					} else if (shapeParams.width) {

						shapeParams.width = realXAxis.toPixels(shapeParams.width) - realXAxis.toPixels(0);

					} else if (defined(shapeParams.x)) {

						shapeParams.x = xAxis.toPixels(shapeParams.x);

					}



					if (defined(shapeParams.y) && shapeParams.height) {

						shapeParams.height = -yAxis.toPixels(shapeParams.height + shapeParams.y) + yAxis.toPixels(shapeParams.y);

						shapeParams.y = yAxis.toPixels(shapeParams.y);

					} else if (shapeParams.height) {

						shapeParams.height = -realYAxis.toPixels(shapeParams.height) + realYAxis.toPixels(0);

						shapeParams.height *= chart.inverted ? -1 : 1;

					} else if (defined(shapeParams.y)) {

						shapeParams.y = yAxis.toPixels(shapeParams.y);

					}



					if (options.shape.type === 'path') {

						shapeParams.d = translatePath(shapeParams.d, xAxis, yAxis, x, y);

					}

				}

				if (defined(options.yValueEnd) && defined(options.xValueEnd)) {

					shapeParams.d = shapeParams.d || options.shape.d || ['M', 0, 0, 'L', 0, 0];

					shapeParams.d[4] = xAxis.toPixels(options.xValueEnd) - xAxis.toPixels(options.xValue);

					shapeParams.d[5] = yAxis.toPixels(options.yValueEnd) - yAxis.toPixels(options.yValue);

				}



				// move the center of the circle to shape x/y

				if (options.shape.type === 'circle') {

					shapeParams.x += shapeParams.r;

					shapeParams.y += shapeParams.r;

				}

				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 (this.selectionMarker) {

				this.events.select({}, this);

			}

			

			if (redraw && chart.animation && defined(group.translateX) && defined(group.translateY)) {

				group.animate({

					translateX: x,

					translateY: y

				});

			} else {

				group.translate(x, y);

			}

		},



		/*

		 * Destroy the annotation

		 */

		destroy: function () {

			var annotation = this,

				chart = this.chart,

				allItems = chart.annotations.allItems,

				index = allItems.indexOf(annotation);



			chart.activeAnnotation = null;

			

			if (index > -1) {

				allItems.splice(index, 1);

				chart.options.annotations.splice(index, 1); // #33

			}



			each(['title', 'shape', 'group'], function (element) {

				if (annotation[element] && annotation[element].destroy) {

					annotation[element].destroy();

					annotation[element] = null;

				} else if (annotation[element]) {

					annotation[element].remove();

					annotation[element] = null;

				}

			});



			annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = annotation.hasEvents = null;

		},



		/*

		 * Show annotation, only for non-linked annotations

		 */

		show: function () {

			if (!this.linkedObject) {

				this.visible = true;

				this.group.attr({

					visibility: 'visible'

				});

			}

		},



		/*

		 * Hide annotation, only for non-linked annotations

		 */

		hide: function () {

			if (!this.linkedObject) {

				this.visible = false;

				this.group.attr({

					visibility: 'hidden'

				});

			}

		},



		/*

		 * Update the annotation with a given options

		 */

		update: function (options, redraw) {

			var annotation = this,

				chart = this.chart,

				allItems = chart.annotations.allItems,

				index = allItems.indexOf(annotation),

				o = merge(this.options, options);

					

			if (index >= 0) {

				chart.options.annotations[index] = o; // #33

			}

									

			this.options = o;



			// update link to point or series

			this.linkObjects();



			this.render(redraw);

			return this;

		},

		/*

		 * API select & deselect:

		 */

		select: function () {

			this.events.select(null, this);

			return this;

		},

		deselect: function () {

			this.events.deselect(null, this);

			this.chart.selectedAnnotation = null;

			return this;

		},





		linkObjects: function () {

			var annotation = this,

				chart = annotation.chart,

				linkedTo = annotation.linkedObject,

				linkedId = linkedTo && (linkedTo.id || linkedTo.options.id),

				options = annotation.options,

				id = options.linkedTo;



			if (!defined(id)) {

				annotation.linkedObject = null;

			} else if (!defined(linkedTo) || id !== linkedId) {

				annotation.linkedObject = chart.get(id);

			}

		},

		events: {

			select: function (e, ann) {

				var chart = ann.chart,

					prevAnn = chart.selectedAnnotation,

					box,

					padding = 10;

				

				if (prevAnn && prevAnn !== ann) {

					prevAnn.deselect();

				}

			

				if (!ann.selectionMarker) {

					box = ann.group.getBBox();

					

					ann.selectionMarker = chart.renderer.rect(

						box.x - padding / 2,

						box.y - padding / 2,

						box.width + padding,

						box.height + padding

					).attr(ann.options.selectionMarker);

					ann.selectionMarker.add(ann.group);

				}

				chart.selectedAnnotation = ann;

			},

			deselect: function () {

				if (this.selectionMarker && this.group) {

					this.selectionMarker.destroy();

					this.selectionMarker = false;

					this.group.bBox = null;

				}

			},

			destroyAnnotation: function (event, annotation) {

				annotation.destroy();

			},

			translateAnnotation: function (event, chart) {

				event.stopPropagation();

				event.preventDefault();

				if (chart.activeAnnotation) {

					var bbox = chart.container.getBoundingClientRect(),

						clickX = event.clientX - bbox.left,

						clickY = event.clientY - bbox.top;

							

					if (!chart.isInsidePlot(clickX - chart.plotLeft, clickY - chart.plotTop)) {

						return;

					}

					var note = chart.activeAnnotation;

							

					var x = note.options.allowDragX ? event.clientX - note.startX + note.group.translateX : note.group.translateX,

						y = note.options.allowDragY ? event.clientY - note.startY + note.group.translateY : note.group.translateY;

				

					note.transX = x;

					note.transY = y;

					note.group.attr({

						transform: 'translate(' + x + ',' + y + ')'

					});

					note.hadMove = true;

				}

			},

			storeAnnotation: function (event, annotation, chart) {

				if (!chart.annotationDraging) {

					event.stopPropagation();

					event.preventDefault();

				}

				if ((!isOldIE && event.button === 0) || (isOldIE && event.button === 1)) {

					var posX = event.clientX,

						posY = event.clientY;

					chart.activeAnnotation = annotation;

					chart.activeAnnotation.startX = posX;

					chart.activeAnnotation.startY = posY;

					chart.activeAnnotation.transX = 0;

					chart.activeAnnotation.transY = 0;

					// translateAnnotation(event);

					addEvent(document, 'mousemove', function (e) {

						annotation.events.translateAnnotation(e, chart);

					});

					// addEvent(chart.container, 'mouseleave', releaseAnnotation); TO BE OR NOT TO BE?

				}

			},

			releaseAnnotation: function (event, chart) {

				event.stopPropagation();

				event.preventDefault();

				if (chart.activeAnnotation && (chart.activeAnnotation.transX !== 0 || chart.activeAnnotation.transY !== 0)) {

					var note = chart.activeAnnotation,

						x = note.transX,

						y = note.transY,

						options = note.options,

						xVal = options.xValue,

						yVal = options.yValue,

						xValEnd = options.xValueEnd,

						yValEnd = options.yValueEnd,

						allowDragX = options.allowDragX,

						allowDragY = options.allowDragY,

						xAxis = note.chart.xAxis[note.options.xAxis],

						yAxis = note.chart.yAxis[note.options.yAxis],

						newX = xAxis.toValue(x),

						newY = yAxis.toValue(y);

					

					if (x !== 0 || y !== 0) {

						if (allowDragX && allowDragY) {

							note.update({

								xValue: defined(xVal) ? newX : null,

								yValue: defined(yVal) ? newY : null,

								xValueEnd: defined(xValEnd) ? xValEnd - xVal + newX : null,

								yValueEnd: defined(yValEnd) ? yValEnd - yVal + newY : null,

								x: defined(xVal) ? null : x,

								y: defined(yVal) ? null : y

							}, false);

						} else if (allowDragX) {

							note.update({

								xValue: defined(xVal) ? newX : null,

								yValue: defined(yVal) ? yVal : null,

								xValueEnd: defined(xValEnd) ? xValEnd - xVal + newX : null,

								yValueEnd: defined(yValEnd) ? yValEnd : null,

								x: defined(xVal) ? null : x,

								y: defined(yVal) ? null : note.options.y

							}, false);

						} else if (allowDragY) {

							note.update({

								xValue: defined(xVal) ? xVal : null,

								yValue: defined(yVal) ? newY : null,

								xValueEnd: defined(xValEnd) ? xValEnd : null,

								yValueEnd: defined(yValEnd) ? yValEnd - yVal + newY : null,

								x: defined(xVal) ? null : note.options.x,

								y: defined(yVal) ? null : y

							}, false);

						}

					}

					chart.activeAnnotation = null;

					chart.redraw(false);

				} else {

					chart.activeAnnotation = null;

				}

			}

		}

	};

	// Add annotations methods to chart prototype

	extend(Chart.prototype, {

		/*

		 * Unified method for adding annotations to the chart

		 */

		addAnnotation: function (options, redraw) {

			var chart = this,

				annotations = chart.annotations.allItems,

				item,

				i,

				iter,

				len;



			if (!isArray(options)) {

				options = [options];

			}



			len = options.length;



			for (iter = 0; iter < len; iter++) {

				item = new Annotation(chart, options[iter]);

				i = annotations.push(item);

				if (i > chart.options.annotations.length) {

					chart.options.annotations.push(options[iter]); // #33

				}

				item.render(redraw);

			}

		},



		/*

		 * Redraw all annotations, method used in chart events

		 */

		redrawAnnotations: function () {

			var chart = this,

				yAxes = chart.yAxis,

				yLen = yAxes.length,

				ann = chart.annotations,

				userOffset = ann.options.buttonsOffsets,

				i = 0,

				clip, y;



				

			for (; i < yLen; i++) {

				y = yAxes[i];

				clip = ann.clipPaths[i];

				

				if (clip) {

					clip.attr({

						x: y.left,

						y: y.top,

						width: y.width,

						height: y.height

					});

				} else {

					var clipPath = createClipPath(chart, y);

					ann.clipPaths.push(clipPath);

					ann.groups.push(createGroup(chart, i, clipPath));

				}

			}

					

			each(chart.annotations.allItems, function (annotation) {

				annotation.redraw();

			});

			each(chart.annotations.buttons, function (button, j) {

				var	xOffset = chart.rangeSelector && chart.rangeSelector.inputGroup ? chart.rangeSelector.inputGroup.offset : 0,

					x = chart.plotWidth + chart.plotLeft - ((j + 1) * 30) - xOffset - userOffset[0];

				button[0].attr({

					x: x

				});

			});

		}

	});





	// Initialize on chart load

	Chart.prototype.callbacks.push(function (chart) {

		var options = chart.options.annotations,

			yAxes = chart.yAxis,

			yLen = yAxes.length,

			clipPaths = [],

			groups = [],

			i = 0,

			y, c;



		for (; i < yLen; i++) {

			y = yAxes[i];

			c = createClipPath(chart, y);

			clipPaths.push(c);

			groups.push(createGroup(chart, i, c));

		}



		if (!chart.annotations) {

			chart.annotations = {};

		}

		

		if (!chart.options.annotations) {

			chart.options.annotations = [];

		}

		

		// initialize empty array for annotations

		if (!chart.annotations.allItems) {

			chart.annotations.allItems = [];

		}



		// allow zoom or draw annotation

		chart.annotations.allowZoom = true;

		

		// link chart object to annotations

		chart.annotations.chart = chart;



		// link annotations group element to the chart

		chart.annotations.groups = groups;

		

		// add clip path to annotations

		chart.annotations.clipPaths = clipPaths;



		if (isArray(options) && options.length > 0) {

			chart.addAnnotation(chart.options.annotations);

		}

		chart.annotations.options = merge(defatultMainOptions(), chart.options.annotationsOptions ? chart.options.annotationsOptions : {});

		

		if (chart.annotations.options.enabledButtons) {

			renderButtons(chart);

			attachEvents(chart);

		} else {

			chart.annotations.buttons = [];

		}



		// update annotations after chart redraw

		addEvent(chart, 'redraw', function () {

			chart.redrawAnnotations();

		});

	});



	if (!Array.prototype.indexOf) {

		Array.prototype.indexOf = function (searchElement) { // eslint-disable-line

			if (this === null) {

				throw new TypeError();

			}

			var t = Object(this);

			var len = t.length >>> 0;

			if (len === 0) {

				return -1;

			}

			var n = 0;

			if (arguments.length > 1) {

				n = Number(arguments[1]);

				if (n !== n) { // shortcut for verifying if it's NaN

					n = 0;

				} else if (n !== 0 && n !== Infinity && n !== -Infinity) {

					n = (n > 0 || -1) * Math.floor(Math.abs(n));

				}

			}

			if (n >= len) {

				return -1;

			}

			var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);

			for (; k < len; k++) {

				if (k in t && t[k] === searchElement) {

					return k;

				}

			}

			return -1;

		};

	}

}));