/** * @license Highcharts JS v5.0.2 (2016-10-26) * * (c) 2009-2016 Torstein Honsi * * License: www.highcharts.com/license */ (function(factory) { if (typeof module === 'object' && module.exports) { module.exports = factory; } else { factory(Highcharts); } }(function(Highcharts) { (function(H) { /** * (c) 2009-2016 Torstein Honsi * * License: www.highcharts.com/license */ 'use strict'; var defined = H.defined, isNumber = H.isNumber, inArray = H.inArray, isArray = H.isArray, merge = H.merge, Chart = H.Chart, extend = H.extend, each = H.each; var ALIGN_FACTOR, ALLOWED_SHAPES; ALLOWED_SHAPES = ['path', 'rect', 'circle']; ALIGN_FACTOR = { top: 0, left: 0, center: 0.5, middle: 0.5, bottom: 1, right: 1 }; 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 translatePath(d, xAxis, yAxis, xOffset, yOffset) { var len = d.length, i = 0; while (i < len) { if (isNumber(d[i]) && isNumber(d[i + 1])) { 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], shapeParams, linkType, series, param, bbox, x, y; if (linkedTo) { linkType = (linkedTo instanceof H.Point) ? 'point' : (linkedTo instanceof H.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 (!isNumber(x) || !isNumber(y)) { return; } if (title) { title.attr(options.title); title.css(options.title.style); } 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; } 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 (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 H.addEvent(chart, 'redraw', function() { chart.annotations.redraw(); }); }); }(Highcharts)); }));