| (function (Highcharts, HighchartsAdapter) { | 
|   | 
| var UNDEFINED, | 
|     ALIGN_FACTOR, | 
|     ALLOWED_SHAPES, | 
|     Chart = Highcharts.Chart, | 
|     extend = Highcharts.extend, | 
|     each = Highcharts.each; | 
|   | 
| ALLOWED_SHAPES = ["path", "rect", "circle"]; | 
|   | 
| ALIGN_FACTOR = { | 
|     top: 0, | 
|     left: 0, | 
|     center: 0.5, | 
|     middle: 0.5, | 
|     bottom: 1, | 
|     right: 1 | 
| }; | 
|   | 
|   | 
| // Highcharts helper methods | 
| var inArray = HighchartsAdapter.inArray, | 
|     merge = Highcharts.merge; | 
|   | 
| function defaultOptions(shapeType) { | 
|     var shapeOptions, | 
|         options; | 
|   | 
|     options = { | 
|         xAxis: 0, | 
|         yAxis: 0, | 
|         title: { | 
|             style: {}, | 
|             text: "", | 
|             x: 0, | 
|             y: 0 | 
|         }, | 
|         shape: { | 
|             params: { | 
|                 stroke: "#000000", | 
|                 fill: "transparent", | 
|                 strokeWidth: 2 | 
|             } | 
|         } | 
|     }; | 
|   | 
|     shapeOptions = { | 
|         circle: { | 
|             params: { | 
|                 x: 0, | 
|                 y: 0 | 
|             } | 
|         } | 
|     }; | 
|   | 
|     if (shapeOptions[shapeType]) { | 
|         options.shape = merge(options.shape, shapeOptions[shapeType]); | 
|     } | 
|   | 
|     return options; | 
| } | 
|   | 
| 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; | 
|   | 
|     while (i < len) { | 
|         if (typeof d[i] === 'number' && typeof d[i + 1] === 'number') { | 
|             d[i] = xAxis.toPixels(d[i]) - xOffset; | 
|             d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset; | 
|             i += 2; | 
|         } else { | 
|             i += 1; | 
|         } | 
|     } | 
|   | 
|     return d; | 
| } | 
|   | 
|   | 
| // Define annotation prototype | 
| var Annotation = function () { | 
|     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); | 
|     }, | 
|   | 
|     /* | 
|      * 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; | 
|   | 
|         if (!group) { | 
|             group = annotation.group = renderer.g(); | 
|         } | 
|   | 
|   | 
|         if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { | 
|             shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); | 
|             shape.add(group); | 
|         } | 
|   | 
|         if (!title && titleOptions) { | 
|             title = annotation.title = renderer.label(titleOptions); | 
|             title.add(group); | 
|         } | 
|   | 
|         group.add(chart.annotations.group); | 
|   | 
|         // link annotations to point or series | 
|         annotation.linkObjects(); | 
|   | 
|         if (redraw !== false) { | 
|             annotation.redraw(); | 
|         } | 
|     }, | 
|   | 
|     /* | 
|      * Redraw the annotation title or shape after options update | 
|      */ | 
|     redraw: 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); | 
|         } | 
|     }, | 
|   | 
|     /* | 
|      * Destroy the annotation | 
|      */ | 
|     destroy: function () { | 
|         var annotation = this, | 
|             chart = this.chart, | 
|             allItems = chart.annotations.allItems, | 
|             index = allItems.indexOf(annotation); | 
|   | 
|         if (index > -1) { | 
|             allItems.splice(index, 1); | 
|         } | 
|   | 
|         each(['title', 'shape', 'group'], function (element) { | 
|             if (annotation[element]) { | 
|                 annotation[element].destroy(); | 
|                 annotation[element] = null; | 
|             } | 
|         }); | 
|   | 
|         annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; | 
|     }, | 
|   | 
|     /* | 
|      * Update the annotation with a given options | 
|      */ | 
|     update: function (options, redraw) { | 
|         extend(this.options, options); | 
|   | 
|         // update link to point or series | 
|         this.linkObjects(); | 
|   | 
|         this.render(redraw); | 
|     }, | 
|   | 
|     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); | 
|         } | 
|     } | 
| }; | 
|   | 
|   | 
| // Add annotations methods to chart prototype | 
| extend(Chart.prototype, { | 
|     annotations: { | 
|         /* | 
|          * Unified method for adding annotations to the chart | 
|          */ | 
|         add: function (options, redraw) { | 
|             var annotations = this.allItems, | 
|                 chart = this.chart, | 
|                 item, | 
|                 len; | 
|   | 
|             if (!isArray(options)) { | 
|                 options = [options]; | 
|             } | 
|   | 
|             len = options.length; | 
|   | 
|             while (len--) { | 
|                 item = new Annotation(chart, options[len]); | 
|                 annotations.push(item); | 
|                 item.render(redraw); | 
|             } | 
|         }, | 
|   | 
|         /** | 
|          * Redraw all annotations, method used in chart events | 
|          */ | 
|         redraw: function () { | 
|             each(this.allItems, function (annotation) { | 
|                 annotation.redraw(); | 
|             }); | 
|         } | 
|     } | 
| }); | 
|   | 
|   | 
| // Initialize on chart load | 
| Chart.prototype.callbacks.push(function (chart) { | 
|     var options = chart.options.annotations, | 
|         group; | 
|   | 
|     group = chart.renderer.g("annotations"); | 
|     group.attr({ | 
|         zIndex: 7 | 
|     }); | 
|     group.add(); | 
|   | 
|     // initialize empty array for annotations | 
|     chart.annotations.allItems = []; | 
|   | 
|     // link chart object to annotations | 
|     chart.annotations.chart = chart; | 
|   | 
|     // link annotations group element to the chart | 
|     chart.annotations.group = group; | 
|   | 
|     if (isArray(options) && options.length > 0) { | 
|         chart.annotations.add(chart.options.annotations); | 
|     } | 
|   | 
|     // update annotations after chart redraw | 
|     Highcharts.addEvent(chart, 'redraw', function () { | 
|         chart.annotations.redraw(); | 
|     }); | 
| }); | 
| }(Highcharts, HighchartsAdapter)); |