123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- (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));
|