You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1686 lines
59 KiB
JavaScript
1686 lines
59 KiB
JavaScript
/**
|
|
* @license Highcharts JS v6.0.4 (2017-12-15)
|
|
* Annotations module
|
|
*
|
|
* (c) 2009-2017 Torstein Honsi
|
|
*
|
|
* License: www.highcharts.com/license
|
|
*/
|
|
'use strict';
|
|
(function(factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory;
|
|
} else {
|
|
factory(Highcharts);
|
|
}
|
|
}(function(Highcharts) {
|
|
(function(H) {
|
|
/**
|
|
* (c) 2009-2017 Highsoft, Black Label
|
|
*
|
|
* License: www.highcharts.com/license
|
|
*/
|
|
|
|
var merge = H.merge,
|
|
addEvent = H.addEvent,
|
|
extend = H.extend,
|
|
each = H.each,
|
|
isString = H.isString,
|
|
isNumber = H.isNumber,
|
|
defined = H.defined,
|
|
isObject = H.isObject,
|
|
inArray = H.inArray,
|
|
erase = H.erase,
|
|
find = H.find,
|
|
format = H.format,
|
|
pick = H.pick,
|
|
destroyObjectProperties = H.destroyObjectProperties,
|
|
|
|
tooltipPrototype = H.Tooltip.prototype,
|
|
seriesPrototype = H.Series.prototype,
|
|
chartPrototype = H.Chart.prototype;
|
|
|
|
|
|
/* ***************************************************************************
|
|
*
|
|
* MARKER SECTION
|
|
* Contains objects and functions for adding a marker element to a path element
|
|
*
|
|
**************************************************************************** */
|
|
|
|
/**
|
|
* Options for configuring markers for annotations.
|
|
*
|
|
* An example of the arrow marker:
|
|
* <pre>
|
|
* {
|
|
* arrow: {
|
|
* id: 'arrow',
|
|
* refY: 5,
|
|
* refX: 5,
|
|
* markerWidth: 10,
|
|
* markerHeight: 10,
|
|
* children: [{
|
|
* tagName: 'path',
|
|
* attrs: {
|
|
* d: 'M 0 0 L 10 5 L 0 10 Z',
|
|
* strokeWidth: 0
|
|
* }
|
|
* }]
|
|
* }
|
|
* }
|
|
* </pre>
|
|
* @type {Object}
|
|
* @sample highcharts/annotations/custom-markers/
|
|
* Define a custom marker for annotations
|
|
* @since 6.0.0
|
|
* @apioption defs.markers
|
|
*/
|
|
var defaultMarkers = {
|
|
arrow: {
|
|
render: false,
|
|
id: 'arrow',
|
|
refY: 5,
|
|
refX: 5,
|
|
markerWidth: 10,
|
|
markerHeight: 10,
|
|
children: [{
|
|
tagName: 'path',
|
|
attrs: {
|
|
d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
|
|
strokeWidth: 0
|
|
}
|
|
}]
|
|
}
|
|
};
|
|
|
|
var MarkerMixin = {
|
|
markerSetter: function(markerType) {
|
|
return function(value) {
|
|
this.attr(markerType, 'url(#' + value + ')');
|
|
};
|
|
}
|
|
};
|
|
|
|
extend(MarkerMixin, {
|
|
markerEndSetter: MarkerMixin.markerSetter('marker-end'),
|
|
markerStartSetter: MarkerMixin.markerSetter('marker-start')
|
|
});
|
|
|
|
|
|
H.SVGRenderer.prototype.addMarker = function(id, markerOptions) {
|
|
var markerId = pick(id, H.uniqueKey()),
|
|
marker = this.createElement('marker').attr({
|
|
id: markerId,
|
|
markerWidth: pick(markerOptions.markerWidth, 20),
|
|
markerHeight: pick(markerOptions.markerHeight, 20),
|
|
refX: markerOptions.refX || 0,
|
|
refY: markerOptions.refY || 0,
|
|
orient: markerOptions.orient || 'auto'
|
|
}).add(this.defs),
|
|
|
|
attrs = {
|
|
stroke: markerOptions.color || 'none',
|
|
fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
|
|
},
|
|
children = markerOptions.children;
|
|
|
|
marker.id = markerId;
|
|
|
|
each(children, function(child) {
|
|
this.createElement(child.tagName)
|
|
.attr(merge(attrs, child.attrs))
|
|
.add(marker);
|
|
}, this);
|
|
|
|
return marker;
|
|
};
|
|
|
|
|
|
/* ***************************************************************************
|
|
*
|
|
* MOCK POINT
|
|
*
|
|
**************************************************************************** */
|
|
|
|
/**
|
|
* A mock point configuration.
|
|
*
|
|
* @typedef {Object} MockPointOptions
|
|
* @property {Number} x - x value for the point in xAxis scale or pixels
|
|
* @property {Number} y - y value for the point in yAxis scale or pixels
|
|
* @property {String|Number} [xAxis] - xAxis index or id
|
|
* @property {String|Number} [yAxis] - yAxis index or id
|
|
*/
|
|
|
|
|
|
/**
|
|
* A trimmed point object which imitates {@link Highchart.Point} class.
|
|
* It is created when there is a need of pointing to some chart's position
|
|
* using axis values or pixel values
|
|
*
|
|
* @class MockPoint
|
|
* @memberOf Highcharts
|
|
*
|
|
* @param {Highcharts.Chart} - the chart object
|
|
* @param {MockPointOptions} - the options object
|
|
*/
|
|
var MockPoint = H.MockPoint = function(chart, options) {
|
|
this.mock = true;
|
|
this.series = {
|
|
visible: true,
|
|
chart: chart,
|
|
getPlotBox: seriesPrototype.getPlotBox
|
|
};
|
|
|
|
// this.plotX
|
|
// this.plotY
|
|
|
|
/* Those might not exist if a specific axis was not found/defined */
|
|
// this.x?
|
|
// this.y?
|
|
|
|
this.init(chart, options);
|
|
};
|
|
|
|
/**
|
|
* A factory function for creating a mock point object
|
|
*
|
|
* @function #mockPoint
|
|
* @memberOf Highcharts
|
|
*
|
|
* @param {MockPointOptions} mockPointOptions
|
|
* @return {MockPoint} a mock point
|
|
*/
|
|
var mockPoint = H.mockPoint = function(chart, mockPointOptions) {
|
|
return new MockPoint(chart, mockPointOptions);
|
|
};
|
|
|
|
MockPoint.prototype = {
|
|
/**
|
|
* Initialisation of the mock point
|
|
*
|
|
* @function #init
|
|
* @memberOf Highcharts.MockPoint#
|
|
*
|
|
* @param {Highcharts.Chart} chart - a chart object to which the mock point
|
|
* is attached
|
|
* @param {MockPointOptions} options - a config for the mock point
|
|
*/
|
|
init: function(chart, options) {
|
|
var xAxisId = options.xAxis,
|
|
xAxis = defined(xAxisId) ?
|
|
chart.xAxis[xAxisId] || chart.get(xAxisId) :
|
|
null,
|
|
|
|
yAxisId = options.yAxis,
|
|
yAxis = defined(yAxisId) ?
|
|
chart.yAxis[yAxisId] || chart.get(yAxisId) :
|
|
null;
|
|
|
|
|
|
if (xAxis) {
|
|
this.x = options.x;
|
|
this.series.xAxis = xAxis;
|
|
} else {
|
|
this.plotX = options.x;
|
|
}
|
|
|
|
if (yAxis) {
|
|
this.y = options.y;
|
|
this.series.yAxis = yAxis;
|
|
} else {
|
|
this.plotY = options.y;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update of the point's coordinates (plotX/plotY)
|
|
*
|
|
* @function #translate
|
|
* @memberOf Highcharts.MockPoint#
|
|
*
|
|
* @return {undefined}
|
|
*/
|
|
translate: function() {
|
|
var series = this.series,
|
|
xAxis = series.xAxis,
|
|
yAxis = series.yAxis,
|
|
plotX = this.plotX,
|
|
plotY = this.plotY,
|
|
isInside = true;
|
|
|
|
if (xAxis) {
|
|
this.plotX = plotX = xAxis.toPixels(this.x, true);
|
|
|
|
isInside = plotX >= 0 && plotX <= xAxis.len;
|
|
}
|
|
|
|
if (yAxis) {
|
|
this.plotY = plotY = yAxis.toPixels(this.y, true);
|
|
|
|
isInside = isInside && plotY >= 0 && plotY <= yAxis.len;
|
|
}
|
|
|
|
this.isInside = isInside;
|
|
},
|
|
|
|
/**
|
|
* Returns a box to which an item can be aligned to
|
|
*
|
|
* @function #alignToBox
|
|
* @memberOf Highcharts.MockPoint#
|
|
*
|
|
* @param {Boolean} [forceTranslate=false] - whether to update the point's
|
|
* coordinates
|
|
* @return {Array.<Number>} A quadruple of numbers which denotes x, y,
|
|
* width and height of the box
|
|
**/
|
|
alignToBox: function(forceTranslate) {
|
|
if (forceTranslate) {
|
|
this.translate();
|
|
}
|
|
|
|
var x = this.plotX,
|
|
y = this.plotY,
|
|
temp;
|
|
|
|
|
|
if (this.series.chart.inverted) {
|
|
temp = x;
|
|
x = y;
|
|
y = temp;
|
|
}
|
|
|
|
return [x, y, 0, 0];
|
|
},
|
|
|
|
/**
|
|
* Returns a label config object -
|
|
* the same as Highcharts.Point.prototype.getLabelConfig
|
|
*
|
|
* @function #getLabelConfig
|
|
* @memberOf Highcharts.MockPoint#
|
|
*
|
|
* @return {Object} labelConfig - label config object
|
|
* @return {Number|undefined} labelConfig.x - x value translated to x axis scale
|
|
* @return {Number|undefined} labelConfig.y - y value translated to y axis scale
|
|
* @return {MockPoint} labelConfig.point - the instance of the point
|
|
*/
|
|
getLabelConfig: function() {
|
|
return {
|
|
x: this.x,
|
|
y: this.y,
|
|
point: this
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
/* ***************************************************************************
|
|
*
|
|
* ANNOTATION
|
|
*
|
|
**************************************************************************** */
|
|
|
|
H.defaultOptions.annotations = [];
|
|
|
|
/**
|
|
* An annotation class which serves as a container for items like labels or shapes.
|
|
* Created items are positioned on the chart either by linking them to
|
|
* existing points or created mock points
|
|
*
|
|
* @class Annotation
|
|
* @memberOf Highcharts
|
|
*
|
|
* @param {Highcharts.Chart} - the chart object
|
|
* @param {AnnotationOptions} - the options object
|
|
*/
|
|
var Annotation = H.Annotation = function(chart, userOptions) {
|
|
this.chart = chart;
|
|
|
|
this.labels = [];
|
|
this.shapes = [];
|
|
|
|
this.options = merge(this.defaultOptions, userOptions);
|
|
|
|
this.init(chart, userOptions);
|
|
};
|
|
|
|
Annotation.prototype = {
|
|
/**
|
|
* Shapes which do not have background - the object is used for proper
|
|
* setting of the contrast color
|
|
*
|
|
* @memberOf Highcharts.Annotation#
|
|
* @type {Array.<String>}
|
|
*/
|
|
shapesWithoutBackground: ['connector'],
|
|
|
|
/**
|
|
* A map object which allows to map options attributes to element attributes
|
|
*
|
|
* @memberOf Highcharts.Annotation#
|
|
* @type {Object}
|
|
*/
|
|
attrsMap: {
|
|
backgroundColor: 'fill',
|
|
borderColor: 'stroke',
|
|
borderWidth: 'stroke-width',
|
|
strokeWidth: 'stroke-width',
|
|
stroke: 'stroke',
|
|
fill: 'fill',
|
|
zIndex: 'zIndex',
|
|
width: 'width',
|
|
height: 'height',
|
|
borderRadius: 'r',
|
|
r: 'r',
|
|
padding: 'padding',
|
|
dashStyle: 'dashstyle'
|
|
},
|
|
|
|
/**
|
|
* Options for configuring annotations, for example labels, arrows or
|
|
* shapes. Annotations can be tied to points, axis coordinates or chart
|
|
* pixel coordinates.
|
|
*
|
|
* @type {Array<Object>}
|
|
* @sample highcharts/annotations/basic/
|
|
* Basic annotations
|
|
* @sample {highstock} stock/annotations/fibonacci-retracements
|
|
* Custom annotation, Fibonacci retracement
|
|
* @since 6.0.0
|
|
* @optionparent annotations
|
|
**/
|
|
defaultOptions: {
|
|
|
|
/**
|
|
* Whether the annotation is visible.
|
|
*
|
|
* @sample highcharts/annotations/visible/
|
|
* Set annotation visibility
|
|
*/
|
|
visible: true,
|
|
|
|
/**
|
|
* Options for annotation's labels. Each label inherits options
|
|
* from the labelOptions object. An option from the labelOptions can be
|
|
* overwritten by config for a specific label.
|
|
*/
|
|
labelOptions: {
|
|
|
|
/**
|
|
* The alignment of the annotation's label. If right,
|
|
* the right side of the label should be touching the point.
|
|
*
|
|
* @validvalue ["left", "center", "right"]
|
|
* @sample highcharts/annotations/label-position/
|
|
* Set labels position
|
|
*/
|
|
align: 'center',
|
|
|
|
/**
|
|
* Whether to allow the annotation's labels to overlap.
|
|
* To make the labels less sensitive for overlapping,
|
|
* the can be set to 0.
|
|
*
|
|
* @sample highcharts/annotations/tooltip-like/
|
|
* Hide overlapping labels
|
|
*/
|
|
allowOverlap: false,
|
|
|
|
/**
|
|
* The background color or gradient for the annotation's label.
|
|
*
|
|
* @type {Color}
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
|
|
|
/**
|
|
* The border color for the annotation's label.
|
|
*
|
|
* @type {Color}
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
borderColor: 'black',
|
|
|
|
/**
|
|
* The border radius in pixels for the annotaiton's label.
|
|
*
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
borderRadius: 1,
|
|
|
|
/**
|
|
* The border width in pixels for the annotation's label
|
|
*
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
borderWidth: 1,
|
|
|
|
/**
|
|
* Whether to hide the annotation's label that is outside the plot area.
|
|
*
|
|
* @sample highcharts/annotations/label-crop-overflow/
|
|
* Crop or justify labels
|
|
*/
|
|
crop: false,
|
|
|
|
/**
|
|
* A [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting) string for the data label.
|
|
*
|
|
* @type {String}
|
|
* @see [plotOptions.series.dataLabels.format](plotOptions.series.dataLabels.format.html)
|
|
* @sample highcharts/annotations/label-text/
|
|
* Set labels text
|
|
* @default undefined
|
|
* @apioption annotations.labelOptions.format
|
|
**/
|
|
|
|
/**
|
|
* Alias for the format option.
|
|
*
|
|
* @type {String}
|
|
* @see [format](annotations.labelOptions.format.html)
|
|
* @sample highcharts/annotations/label-text/
|
|
* Set labels text
|
|
* @default undefined
|
|
* @apioption annotations.labelOptions.text
|
|
*/
|
|
|
|
/**
|
|
* Callback JavaScript function to format the annotation's label. Note that
|
|
* if a `format` or `text` are defined, the format or text take precedence
|
|
* and the formatter is ignored. `This` refers to a point object.
|
|
*
|
|
* @type {Function}
|
|
* @sample highcharts/annotations/label-text/
|
|
* Set labels text
|
|
* @default function () {
|
|
* return defined(this.y) ? this.y : 'Annotation label';
|
|
* }
|
|
**/
|
|
formatter: function() {
|
|
return defined(this.y) ? this.y : 'Annotation label';
|
|
},
|
|
|
|
/**
|
|
* How to handle the annotation's label that flow outside the plot
|
|
* area. The justify option aligns the label inside the plot area.
|
|
*
|
|
* @validvalue ["none", "justify"]
|
|
* @sample highcharts/annotations/label-crop-overflow/
|
|
* Crop or justify labels
|
|
**/
|
|
overflow: 'justify',
|
|
|
|
/**
|
|
* When either the borderWidth or the backgroundColor is set,
|
|
* this is the padding within the box.
|
|
*
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
padding: 5,
|
|
|
|
/**
|
|
* The shadow of the box. The shadow can be an object configuration
|
|
* containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
|
|
*
|
|
* @type {Boolean|Object}
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
*/
|
|
shadow: false,
|
|
|
|
/**
|
|
* The name of a symbol to use for the border around the label.
|
|
* Symbols are predefined functions on the Renderer object.
|
|
*
|
|
* @type {String}
|
|
* @sample highcharts/annotations/shapes/
|
|
* Available shapes for labels
|
|
**/
|
|
shape: 'callout',
|
|
|
|
/**
|
|
* Styles for the annotation's label.
|
|
*
|
|
* @type {CSSObject}
|
|
* @sample highcharts/annotations/label-presentation/
|
|
* Set labels graphic options
|
|
* @see [plotOptions.series.dataLabels.style](plotOptions.series.dataLabels.style.html)
|
|
**/
|
|
style: {
|
|
fontSize: '11px',
|
|
fontWeight: 'normal',
|
|
color: 'contrast'
|
|
},
|
|
|
|
/**
|
|
* Whether to [use HTML](http://www.highcharts.com/docs/chart-concepts/labels-
|
|
* and-string-formatting#html) to render the annotation's label.
|
|
*
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
useHTML: false,
|
|
|
|
/**
|
|
* The vertical alignment of the annotation's label.
|
|
*
|
|
* @type {String}
|
|
* @validvalue ["top", "middle", "bottom"]
|
|
* @sample highcharts/annotations/label-position/
|
|
* Set labels position
|
|
**/
|
|
verticalAlign: 'bottom',
|
|
|
|
/**
|
|
* The x position offset of the label relative to the point.
|
|
* Note that if a `distance` is defined, the distance takes
|
|
* precedence over `x` and `y` options.
|
|
*
|
|
* @sample highcharts/annotations/label-position/
|
|
* Set labels position
|
|
**/
|
|
x: 0,
|
|
|
|
/**
|
|
* The y position offset of the label relative to the point.
|
|
* Note that if a `distance` is defined, the distance takes
|
|
* precedence over `x` and `y` options.
|
|
*
|
|
* @sample highcharts/annotations/label-position/
|
|
* Set labels position
|
|
**/
|
|
y: -16
|
|
|
|
/**
|
|
* The label's pixel distance from the point.
|
|
*
|
|
* @type {Number}
|
|
* @sample highcharts/annotations/label-position/
|
|
* Set labels position
|
|
* @default undefined
|
|
* @apioption annotations.labelOptions.distance
|
|
**/
|
|
},
|
|
|
|
/**
|
|
* An array of labels for the annotation. For options that apply to multiple
|
|
* labels, they can be added to the [labelOptions](annotations.labelOptions.html).
|
|
*
|
|
* @type {Array<Object>}
|
|
* @extends annotations.labelOptions
|
|
* @apioption annotations.labels
|
|
*/
|
|
|
|
/**
|
|
* This option defines the point to which the label will be connected.
|
|
* It can be either the point which exists in the series - it is referenced
|
|
* by the point's id - or a new point with defined x, y properies
|
|
* and optionally axes.
|
|
*
|
|
* @type {String|Object}
|
|
* @sample highcharts/annotations/mock-point/
|
|
* Attach annotation to a mock point
|
|
* @apioption annotations.labels.point
|
|
*/
|
|
|
|
/**
|
|
* The x position of the point. Units can be either in axis
|
|
* or chart pixel coordinates.
|
|
*
|
|
* @type {Number}
|
|
* @apioption annotations.labels.point.x
|
|
*/
|
|
|
|
/**
|
|
* The y position of the point. Units can be either in axis
|
|
* or chart pixel coordinates.
|
|
*
|
|
* @type {Number}
|
|
* @apioption annotations.labels.point.y
|
|
*/
|
|
|
|
/**
|
|
* This number defines which xAxis the point is connected to. It refers
|
|
* to either the axis id or the index of the axis in the xAxis array.
|
|
* If the option is not configured or the axis is not found the point's
|
|
* x coordinate refers to the chart pixels.
|
|
*
|
|
* @type {Number|String}
|
|
* @apioption annotations.labels.point.xAxis
|
|
*/
|
|
|
|
/**
|
|
* This number defines which yAxis the point is connected to. It refers
|
|
* to either the axis id or the index of the axis in the yAxis array.
|
|
* If the option is not configured or the axis is not found the point's
|
|
* y coordinate refers to the chart pixels.
|
|
*
|
|
* @type {Number|String}
|
|
* @apioption annotations.labels.point.yAxis
|
|
*/
|
|
|
|
|
|
/**
|
|
* Options for annotation's shapes. Each shape inherits options
|
|
* from the shapeOptions object. An option from the shapeOptions can be
|
|
* overwritten by config for a specific shape.
|
|
*
|
|
* @type {Object}
|
|
**/
|
|
shapeOptions: {
|
|
|
|
/**
|
|
* The color of the shape's stroke.
|
|
*
|
|
* @type {Color}
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
**/
|
|
stroke: 'rgba(0, 0, 0, 0.75)',
|
|
|
|
/**
|
|
* The pixel stroke width of the shape.
|
|
*
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
**/
|
|
strokeWidth: 1,
|
|
|
|
/**
|
|
* The color of the shape's fill.
|
|
*
|
|
* @type {Color}
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
**/
|
|
fill: 'rgba(0, 0, 0, 0.75)',
|
|
|
|
/**
|
|
* The type of the shape, e.g. circle or rectangle.
|
|
*
|
|
* @type {String}
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
* @default 'rect'
|
|
* @apioption annotations.shapeOptions.type
|
|
**/
|
|
|
|
/**
|
|
* The radius of the shape.
|
|
*
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
**/
|
|
r: 0
|
|
|
|
/**
|
|
* The width of the shape.
|
|
*
|
|
* @type {Number}
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
* @apioption annotations.shapeOptions.width
|
|
**/
|
|
|
|
/**
|
|
* The height of the shape.
|
|
*
|
|
* @type {Number}
|
|
* @sample highcharts/annotations/shape/
|
|
* Basic shape annotation
|
|
* @apioption annotations.shapeOptions.height
|
|
**/
|
|
},
|
|
|
|
/**
|
|
* The Z index of the annotation.
|
|
*
|
|
* @type {Number}
|
|
* @default 6
|
|
**/
|
|
zIndex: 6
|
|
|
|
/**
|
|
* An array of shapes for the annotation. For options that apply to multiple
|
|
* shapes, then can be added to the [shapeOptions](annotations.shapeOptions.html).
|
|
*
|
|
* @type {Array<Object>}
|
|
* @extends annotations.shapeOptions
|
|
* @apioption annotations.shapes
|
|
*/
|
|
|
|
/**
|
|
* This option defines the point to which the shape will be connected.
|
|
* It can be either the point which exists in the series - it is referenced
|
|
* by the point's id - or a new point with defined x, y properties
|
|
* and optionally axes.
|
|
*
|
|
* @type {String|Object}
|
|
* @extends annotations.labels.point
|
|
* @apioption annotations.shapes.point
|
|
*/
|
|
|
|
/**
|
|
* An array of points for the shape. This option is available for shapes
|
|
* which can use multiple points such as path. A point can be either
|
|
* a point object or a point's id.
|
|
*
|
|
* @type {Array}
|
|
* @see [annotations.shapes.point](annotations.shapes.point.html)
|
|
* @apioption annotations.shapes.points
|
|
*/
|
|
|
|
/**
|
|
* Id of the marker which will be drawn at the final vertex of the path.
|
|
* Custom markers can be defined in defs property.
|
|
*
|
|
* @type {String}
|
|
* @see [defs.markers](defs.markers.html)
|
|
* @sample highcharts/annotations/custom-markers/
|
|
* Define a custom marker for annotations
|
|
* @apioption annotations.shapes.markerEnd
|
|
**/
|
|
|
|
/**
|
|
* Id of the marker which will be drawn at the first vertex of the path.
|
|
* Custom markers can be defined in defs property.
|
|
*
|
|
* @type {String}
|
|
* @see [defs.markers](defs.markers.html)
|
|
* @sample {highcharts} highcharts/annotations/custom-markers/
|
|
* Define a custom marker for annotations
|
|
* @apioption annotations.shapes.markerStart
|
|
**/
|
|
},
|
|
|
|
/**
|
|
* Annotation initialisation
|
|
*
|
|
* @function #init
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @return {undefined}
|
|
**/
|
|
init: function() {
|
|
var anno = this;
|
|
each(this.options.labels || [], this.initLabel, this);
|
|
each(this.options.shapes || [], this.initShape, this);
|
|
|
|
// Push the callback that reports to the overlapping-labels module which
|
|
// labels it should account for.
|
|
this.chart.labelCollectors.push(function() {
|
|
var labels = [];
|
|
each(anno.labels, function(label) {
|
|
if (!label.options.allowOverlap) {
|
|
labels.push(label);
|
|
}
|
|
});
|
|
return labels;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Main method for drawing an annotation, it is called everytime on chart redraw
|
|
* and once on chart's load
|
|
*
|
|
* @function #redraw
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @return {undefined}
|
|
**/
|
|
redraw: function() {
|
|
if (!this.group) {
|
|
this.render();
|
|
}
|
|
|
|
this.redrawItems(this.shapes);
|
|
this.redrawItems(this.labels);
|
|
},
|
|
|
|
/**
|
|
* @function #redrawItems
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Array<Object>} items
|
|
* @return {undefined}
|
|
**/
|
|
redrawItems: function(items) {
|
|
var i = items.length;
|
|
|
|
// needs a backward loop
|
|
// labels/shapes array might be modified due to destruction of the item
|
|
while (i--) {
|
|
this.redrawItem(items[i]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @function #render
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @return {undefined}
|
|
**/
|
|
render: function() {
|
|
var renderer = this.chart.renderer;
|
|
|
|
var group = this.group = renderer.g('annotation')
|
|
.attr({
|
|
zIndex: this.options.zIndex,
|
|
visibility: this.options.visible ? 'visible' : 'hidden'
|
|
})
|
|
.add();
|
|
|
|
this.shapesGroup = renderer.g('annotation-shapes').add(group);
|
|
this.labelsGroup = renderer.g('annotation-labels').attr({
|
|
// hideOverlappingLabels requires translation
|
|
translateX: 0,
|
|
translateY: 0
|
|
}).add(group);
|
|
|
|
this.shapesGroup.clip(this.chart.plotBoxClip);
|
|
},
|
|
|
|
/**
|
|
* @function #setVisible
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Boolean} [visibility] - whether to show or hide an annotation.
|
|
* If the param is omitted, the annotation's visibility is toggled
|
|
* @return {undefined}
|
|
**/
|
|
setVisible: function(visibility) {
|
|
var options = this.options,
|
|
visible = pick(visibility, !options.visible);
|
|
|
|
this.group.attr({
|
|
visibility: visible ? 'visible' : 'hidden'
|
|
});
|
|
|
|
options.visible = visible;
|
|
},
|
|
|
|
|
|
/**
|
|
* Destroying an annotation
|
|
*
|
|
* @function #destroy
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @return {undefined}
|
|
**/
|
|
destroy: function() {
|
|
var chart = this.chart;
|
|
|
|
each(this.labels, function(label) {
|
|
label.destroy();
|
|
});
|
|
|
|
each(this.shapes, function(shape) {
|
|
shape.destroy();
|
|
});
|
|
|
|
destroyObjectProperties(this, chart);
|
|
},
|
|
|
|
|
|
/* ***********************************************************************
|
|
* ITEM SECTION
|
|
* Contains methods for handling a single item in an annotation
|
|
*********************************************************************** */
|
|
|
|
/**
|
|
* Initialisation of a single shape
|
|
*
|
|
* @function #initShape
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} shapeOptions - a confg object for a single shape
|
|
* @return {undefined}
|
|
**/
|
|
initShape: function(shapeOptions) {
|
|
var renderer = this.chart.renderer,
|
|
options = merge(this.options.shapeOptions, shapeOptions),
|
|
attr = this.attrsFromOptions(options),
|
|
|
|
type = renderer[options.type] ? options.type : 'rect',
|
|
shape = renderer[type](0, -9e9, 0, 0);
|
|
|
|
shape.points = [];
|
|
shape.type = type;
|
|
shape.options = options;
|
|
shape.itemType = 'shape';
|
|
|
|
if (type === 'path') {
|
|
extend(shape, {
|
|
markerStartSetter: MarkerMixin.markerStartSetter,
|
|
markerEndSetter: MarkerMixin.markerEndSetter,
|
|
markerStart: MarkerMixin.markerStart,
|
|
markerEnd: MarkerMixin.markerEnd
|
|
});
|
|
}
|
|
|
|
shape.attr(attr);
|
|
|
|
this.shapes.push(shape);
|
|
},
|
|
|
|
/**
|
|
* Initialisation of a single label
|
|
*
|
|
* @function #initShape
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} labelOptions
|
|
* @return {undefined}
|
|
**/
|
|
initLabel: function(labelOptions) {
|
|
var options = merge(this.options.labelOptions, labelOptions),
|
|
style = options.style,
|
|
attr = this.attrsFromOptions(options),
|
|
|
|
label = this.chart.renderer.label(
|
|
'',
|
|
0, -9e9,
|
|
options.shape,
|
|
null,
|
|
null,
|
|
options.useHTML,
|
|
null,
|
|
'annotation-label'
|
|
);
|
|
|
|
if (style.color === 'contrast') {
|
|
style.color = this.chart.renderer.getContrast(
|
|
inArray(options.shape, this.shapesWithoutBackground) > -1 ?
|
|
'#FFFFFF' :
|
|
options.backgroundColor
|
|
);
|
|
}
|
|
|
|
label.points = [];
|
|
label.options = options;
|
|
label.itemType = 'label';
|
|
|
|
// Labelrank required for hideOverlappingLabels()
|
|
label.labelrank = options.labelrank;
|
|
label.annotation = this;
|
|
|
|
label.attr(attr).css(style).shadow(options.shadow);
|
|
|
|
this.labels.push(label);
|
|
},
|
|
|
|
/**
|
|
* Redrawing a single item
|
|
*
|
|
* @function #redrawItem
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @return {undefined}
|
|
*/
|
|
redrawItem: function(item) {
|
|
var points = this.linkPoints(item),
|
|
itemOptions = item.options,
|
|
text;
|
|
|
|
if (!points.length) {
|
|
this.destroyItem(item);
|
|
|
|
} else {
|
|
if (!item.parentGroup) {
|
|
this.renderItem(item);
|
|
}
|
|
|
|
if (item.itemType === 'label') {
|
|
text = itemOptions.format || itemOptions.text;
|
|
item.attr({
|
|
text: text ?
|
|
format(text, points[0].getLabelConfig()) : itemOptions.formatter.call(points[0])
|
|
});
|
|
}
|
|
|
|
|
|
if (item.type === 'path') {
|
|
this.redrawPath(item);
|
|
|
|
} else {
|
|
this.alignItem(item, !item.placed);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Destroing a single item
|
|
*
|
|
* @function #destroyItem
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @return {undefined}
|
|
*/
|
|
destroyItem: function(item) {
|
|
// erase from shapes or labels array
|
|
erase(this[item.itemType + 's'], item);
|
|
item.destroy();
|
|
},
|
|
|
|
/**
|
|
* Returns a point object
|
|
*
|
|
* @function #pointItem
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} pointOptions
|
|
* @param {Highcharts.MockPoint|Highcharts.Point} point
|
|
* @return {Highcharts.MockPoint|Highcharts.Point|null} if the point is
|
|
* found/exists returns this point, otherwise null
|
|
*/
|
|
pointItem: function(pointOptions, point) {
|
|
if (!point || point.series === null) {
|
|
if (isObject(pointOptions)) {
|
|
point = mockPoint(this.chart, pointOptions);
|
|
|
|
} else if (isString(pointOptions)) {
|
|
point = this.chart.get(pointOptions) || null;
|
|
}
|
|
}
|
|
|
|
return point;
|
|
},
|
|
|
|
/**
|
|
* Linking item with the point or points and returning an array of linked points
|
|
*
|
|
* @function #linkPoints
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @return {
|
|
* Highcharts.Point|
|
|
* Highcharts.MockPoint|
|
|
* Array<Highcharts.Point|Highcharts.MockPoint>
|
|
* }
|
|
*/
|
|
linkPoints: function(item) {
|
|
var pointsOptions = item.options.points || (item.options.point && H.splat(item.options.point)),
|
|
points = item.points,
|
|
len = pointsOptions && pointsOptions.length,
|
|
i,
|
|
point;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
point = this.pointItem(pointsOptions[i], points[i]);
|
|
|
|
if (!point) {
|
|
return (item.points = []);
|
|
}
|
|
|
|
points[i] = point;
|
|
}
|
|
|
|
return points;
|
|
},
|
|
|
|
/**
|
|
* Aligning the item and setting its anchor
|
|
*
|
|
* @function #alignItem
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @param {Boolean} isNew - if the label is re-positioned (is not new) it is animated
|
|
* @return {undefined}
|
|
*/
|
|
alignItem: function(item, isNew) {
|
|
var anchor = this.itemAnchor(item, item.points[0]),
|
|
attrs = this.itemPosition(item, anchor);
|
|
|
|
if (attrs) {
|
|
item.alignAttr = attrs;
|
|
item.placed = true;
|
|
|
|
attrs.anchorX = anchor.absolutePosition.x;
|
|
attrs.anchorY = anchor.absolutePosition.y;
|
|
|
|
item[isNew ? 'attr' : 'animate'](attrs);
|
|
|
|
} else {
|
|
item.placed = false;
|
|
|
|
item.attr({
|
|
x: 0,
|
|
y: -9e9
|
|
});
|
|
}
|
|
},
|
|
|
|
redrawPath: function(pathItem, isNew) {
|
|
var points = pathItem.points,
|
|
strokeWidth = pathItem['stroke-width'],
|
|
d = ['M'],
|
|
pointIndex = 0,
|
|
dIndex = 0,
|
|
len = points && points.length,
|
|
crispSegmentIndex,
|
|
anchor,
|
|
point,
|
|
showPath;
|
|
|
|
if (len) {
|
|
do {
|
|
point = points[pointIndex];
|
|
|
|
anchor = this.itemAnchor(pathItem, point).absolutePosition;
|
|
d[++dIndex] = anchor.x;
|
|
d[++dIndex] = anchor.y;
|
|
|
|
// crisping line, it might be replaced with Renderer.prototype.crispLine
|
|
// but it requires creating many temporary arrays
|
|
crispSegmentIndex = dIndex % 5;
|
|
if (crispSegmentIndex === 0) {
|
|
if (d[crispSegmentIndex + 1] === d[crispSegmentIndex + 4]) {
|
|
d[crispSegmentIndex + 1] = d[crispSegmentIndex + 4] = Math.round(d[crispSegmentIndex + 1]) - (strokeWidth % 2 / 2);
|
|
}
|
|
|
|
if (d[crispSegmentIndex + 2] === d[crispSegmentIndex + 5]) {
|
|
d[crispSegmentIndex + 2] = d[crispSegmentIndex + 5] = Math.round(d[crispSegmentIndex + 2]) + (strokeWidth % 2 / 2);
|
|
}
|
|
}
|
|
|
|
if (pointIndex < len - 1) {
|
|
d[++dIndex] = 'L';
|
|
}
|
|
|
|
showPath = point.series.visible;
|
|
|
|
} while (++pointIndex < len && showPath);
|
|
}
|
|
|
|
|
|
if (showPath) {
|
|
pathItem[isNew ? 'attr' : 'animate']({
|
|
d: d
|
|
});
|
|
|
|
} else {
|
|
pathItem.attr({
|
|
d: 'M 0 ' + -9e9
|
|
});
|
|
}
|
|
|
|
pathItem.placed = showPath;
|
|
},
|
|
|
|
renderItem: function(item) {
|
|
item.add(item.itemType === 'label' ? this.labelsGroup : this.shapesGroup);
|
|
|
|
this.setItemMarkers(item);
|
|
},
|
|
|
|
setItemMarkers: function(item) {
|
|
var itemOptions = item.options,
|
|
chart = this.chart,
|
|
markers = chart.options.defs.markers,
|
|
fill = itemOptions.fill,
|
|
color = defined(fill) && fill !== 'none' ? fill : itemOptions.stroke,
|
|
|
|
|
|
setMarker = function(markerType) {
|
|
var markerId = itemOptions[markerType],
|
|
marker,
|
|
predefinedMarker,
|
|
key;
|
|
|
|
if (markerId) {
|
|
for (key in markers) {
|
|
marker = markers[key];
|
|
if (markerId === marker.id) {
|
|
predefinedMarker = marker;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (predefinedMarker) {
|
|
marker = item[markerType] = chart.renderer.addMarker(
|
|
null,
|
|
merge(predefinedMarker, {
|
|
color: color
|
|
})
|
|
);
|
|
|
|
item.attr(markerType, marker.id);
|
|
}
|
|
}
|
|
};
|
|
|
|
each(['markerStart', 'markerEnd'], setMarker);
|
|
},
|
|
|
|
/**
|
|
* An object which denotes an anchor position
|
|
*
|
|
* @typedef {Object} AnchorPosition
|
|
* @property {Number} AnchorPosition.x
|
|
* @property {Number} AnchorPosition.y
|
|
* @property {Number} AnchorPosition.height
|
|
* @property {Number} AnchorPosition.width
|
|
*/
|
|
|
|
/**
|
|
* Returns object which denotes anchor position - relative and absolute
|
|
*
|
|
* @function #itemAnchor
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @param {Highcharts.Point|Highcharts.MockPoint} point
|
|
* @return {Object} anchor
|
|
* @return {AnchorPosition} anchor.relativePosition - relative to the plot area position
|
|
* @return {AnchorPosition} anchor.absolutePosition - absolute position
|
|
*/
|
|
itemAnchor: function(item, point) {
|
|
var plotBox = point.series.getPlotBox(),
|
|
|
|
box = point.mock ?
|
|
point.alignToBox(true) :
|
|
tooltipPrototype.getAnchor.call({
|
|
chart: this.chart
|
|
}, point),
|
|
|
|
anchor = {
|
|
x: box[0],
|
|
y: box[1],
|
|
height: box[2] || 0,
|
|
width: box[3] || 0
|
|
};
|
|
|
|
return {
|
|
relativePosition: anchor,
|
|
absolutePosition: merge(anchor, {
|
|
x: anchor.x + plotBox.translateX,
|
|
y: anchor.y + plotBox.translateY
|
|
})
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Returns the item position
|
|
*
|
|
* @function #itemPosition
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} item
|
|
* @param {AnchorPosition} anchor
|
|
* @return {Object|null} position
|
|
* @return {Number} position.x
|
|
* @return {Number} position.y
|
|
*/
|
|
itemPosition: function(item, anchor) {
|
|
var chart = this.chart,
|
|
point = item.points[0],
|
|
itemOptions = item.options,
|
|
anchorAbsolutePosition = anchor.absolutePosition,
|
|
anchorRelativePosition = anchor.relativePosition,
|
|
itemPosition,
|
|
alignTo,
|
|
itemPosRelativeX,
|
|
itemPosRelativeY,
|
|
|
|
showItem = point.series.visible && point.isInside !== false;
|
|
|
|
if (showItem) {
|
|
|
|
if (defined(itemOptions.distance) || itemOptions.positioner) {
|
|
itemPosition = (itemOptions.positioner || tooltipPrototype.getPosition).call({
|
|
chart: chart,
|
|
distance: pick(itemOptions.distance, 16)
|
|
},
|
|
item.width,
|
|
item.height, {
|
|
plotX: anchorRelativePosition.x,
|
|
plotY: anchorRelativePosition.y,
|
|
negative: point.negative,
|
|
ttBelow: point.ttBelow,
|
|
h: anchorRelativePosition.height || anchorRelativePosition.width
|
|
}
|
|
);
|
|
|
|
} else {
|
|
alignTo = {
|
|
x: anchorAbsolutePosition.x,
|
|
y: anchorAbsolutePosition.y,
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
|
|
itemPosition = this.alignedPosition(
|
|
extend(itemOptions, {
|
|
width: item.width,
|
|
height: item.height
|
|
}),
|
|
alignTo
|
|
);
|
|
|
|
if (item.options.overflow === 'justify') {
|
|
itemPosition = this.alignedPosition(
|
|
this.justifiedOptions(item, itemOptions, itemPosition),
|
|
alignTo
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
if (itemOptions.crop) {
|
|
itemPosRelativeX = itemPosition.x - chart.plotLeft;
|
|
itemPosRelativeY = itemPosition.y - chart.plotTop;
|
|
|
|
showItem =
|
|
chart.isInsidePlot(itemPosRelativeX, itemPosRelativeY) &&
|
|
chart.isInsidePlot(
|
|
itemPosRelativeX + item.width,
|
|
itemPosRelativeY + item.height
|
|
);
|
|
}
|
|
}
|
|
|
|
return showItem ? itemPosition : null;
|
|
},
|
|
|
|
/**
|
|
* Returns new aligned position based alignment options and box to align to.
|
|
* It is almost a one-to-one copy from SVGElement.prototype.align
|
|
* except it does not use and mutate an element
|
|
*
|
|
* @function #alignedPosition
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} alignOptions
|
|
* @param {Object} box
|
|
* @return {Object} aligned position
|
|
**/
|
|
alignedPosition: function(alignOptions, box) {
|
|
var align = alignOptions.align,
|
|
vAlign = alignOptions.verticalAlign,
|
|
x = (box.x || 0) + (alignOptions.x || 0),
|
|
y = (box.y || 0) + (alignOptions.y || 0),
|
|
|
|
alignFactor,
|
|
vAlignFactor;
|
|
|
|
if (align === 'right') {
|
|
alignFactor = 1;
|
|
} else if (align === 'center') {
|
|
alignFactor = 2;
|
|
}
|
|
if (alignFactor) {
|
|
x += (box.width - (alignOptions.width || 0)) / alignFactor;
|
|
}
|
|
|
|
if (vAlign === 'bottom') {
|
|
vAlignFactor = 1;
|
|
} else if (vAlign === 'middle') {
|
|
vAlignFactor = 2;
|
|
}
|
|
if (vAlignFactor) {
|
|
y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
|
|
}
|
|
|
|
return {
|
|
x: Math.round(x),
|
|
y: Math.round(y)
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Returns new alignment options for a label if the label is outside the plot area.
|
|
* It is almost a one-to-one copy from Series.prototype.justifyDataLabel
|
|
* except it does not mutate the label and it works with absolute instead of relative position
|
|
*
|
|
* @function #justifiedOptions
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} label
|
|
* @param {Object} alignOptions
|
|
* @param {Object} alignAttr
|
|
* @return {Object} justified options
|
|
**/
|
|
justifiedOptions: function(label, alignOptions, alignAttr) {
|
|
var chart = this.chart,
|
|
align = alignOptions.align,
|
|
verticalAlign = alignOptions.verticalAlign,
|
|
padding = label.box ? 0 : (label.padding || 0),
|
|
bBox = label.getBBox(),
|
|
off,
|
|
|
|
options = {
|
|
align: align,
|
|
verticalAlign: verticalAlign,
|
|
x: alignOptions.x,
|
|
y: alignOptions.y,
|
|
width: label.width,
|
|
height: label.height
|
|
},
|
|
|
|
x = alignAttr.x - chart.plotLeft,
|
|
y = alignAttr.y - chart.plotTop;
|
|
|
|
// Off left
|
|
off = x + padding;
|
|
if (off < 0) {
|
|
if (align === 'right') {
|
|
options.align = 'left';
|
|
} else {
|
|
options.x = -off;
|
|
}
|
|
}
|
|
|
|
// Off right
|
|
off = x + bBox.width - padding;
|
|
if (off > chart.plotWidth) {
|
|
if (align === 'left') {
|
|
options.align = 'right';
|
|
} else {
|
|
options.x = chart.plotWidth - off;
|
|
}
|
|
}
|
|
|
|
// Off top
|
|
off = y + padding;
|
|
if (off < 0) {
|
|
if (verticalAlign === 'bottom') {
|
|
options.verticalAlign = 'top';
|
|
} else {
|
|
options.y = -off;
|
|
}
|
|
}
|
|
|
|
// Off bottom
|
|
off = y + bBox.height - padding;
|
|
if (off > chart.plotHeight) {
|
|
if (verticalAlign === 'top') {
|
|
options.verticalAlign = 'bottom';
|
|
} else {
|
|
options.y = chart.plotHeight - off;
|
|
}
|
|
}
|
|
|
|
return options;
|
|
},
|
|
|
|
|
|
/**
|
|
* Utility function for mapping item's options to element's attribute
|
|
*
|
|
* @function #attrsFromOptions
|
|
* @memberOf Highcharts.Annotation#
|
|
*
|
|
* @param {Object} options
|
|
* @return {Object} mapped options
|
|
**/
|
|
attrsFromOptions: function(options) {
|
|
var map = this.attrsMap,
|
|
attrs = {},
|
|
key,
|
|
mappedKey;
|
|
|
|
for (key in options) {
|
|
mappedKey = map[key];
|
|
if (mappedKey) {
|
|
attrs[mappedKey] = options[key];
|
|
}
|
|
}
|
|
|
|
return attrs;
|
|
}
|
|
};
|
|
|
|
/* ***************************************************************************
|
|
*
|
|
* EXTENDING CHART PROTOTYPE
|
|
*
|
|
**************************************************************************** */
|
|
|
|
H.extend(chartPrototype, {
|
|
addAnnotation: function(userOptions, redraw) {
|
|
var annotation = new Annotation(this, userOptions);
|
|
|
|
this.annotations.push(annotation);
|
|
|
|
if (pick(redraw, true)) {
|
|
annotation.redraw();
|
|
}
|
|
|
|
return annotation;
|
|
},
|
|
|
|
removeAnnotation: function(id) {
|
|
var annotations = this.annotations,
|
|
annotation = find(annotations, function(annotation) {
|
|
return annotation.options.id === id;
|
|
});
|
|
|
|
if (annotation) {
|
|
erase(annotations, annotation);
|
|
annotation.destroy();
|
|
}
|
|
},
|
|
|
|
drawAnnotations: function() {
|
|
var clip = this.plotBoxClip,
|
|
plotBox = this.plotBox;
|
|
|
|
if (clip) {
|
|
clip.attr(plotBox);
|
|
} else {
|
|
this.plotBoxClip = this.renderer.clipRect(plotBox);
|
|
}
|
|
|
|
each(this.annotations, function(annotation) {
|
|
annotation.redraw();
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
chartPrototype.callbacks.push(function(chart) {
|
|
chart.annotations = [];
|
|
|
|
each(chart.options.annotations, function(annotationOptions) {
|
|
chart.addAnnotation(annotationOptions, false);
|
|
});
|
|
|
|
chart.drawAnnotations();
|
|
addEvent(chart, 'redraw', chart.drawAnnotations);
|
|
addEvent(chart, 'destroy', function() {
|
|
var plotBoxClip = chart.plotBoxClip;
|
|
|
|
if (plotBoxClip && plotBoxClip.destroy) {
|
|
plotBoxClip.destroy();
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
H.wrap(chartPrototype, 'getContainer', function(p) {
|
|
p.call(this);
|
|
|
|
var renderer = this.renderer,
|
|
options = this.options,
|
|
key,
|
|
markers,
|
|
marker;
|
|
|
|
options.defs = merge(options.defs || {}, {
|
|
markers: defaultMarkers
|
|
});
|
|
markers = options.defs.markers;
|
|
|
|
for (key in markers) {
|
|
marker = markers[key];
|
|
|
|
if (pick(marker.render, true)) {
|
|
renderer.addMarker(marker.id, marker);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
/**
|
|
* General symbol definition for labels with connector
|
|
*/
|
|
H.SVGRenderer.prototype.symbols.connector = function(x, y, w, h, options) {
|
|
var anchorX = options && options.anchorX,
|
|
anchorY = options && options.anchorY,
|
|
path,
|
|
yOffset,
|
|
lateral = w / 2;
|
|
|
|
if (isNumber(anchorX) && isNumber(anchorY)) {
|
|
|
|
path = ['M', anchorX, anchorY];
|
|
|
|
// Prefer 45 deg connectors
|
|
yOffset = y - anchorY;
|
|
if (yOffset < 0) {
|
|
yOffset = -h - yOffset;
|
|
}
|
|
if (yOffset < w) {
|
|
lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
|
|
}
|
|
|
|
// Anchor below label
|
|
if (anchorY > y + h) {
|
|
path.push('L', x + lateral, y + h);
|
|
|
|
// Anchor above label
|
|
} else if (anchorY < y) {
|
|
path.push('L', x + lateral, y);
|
|
|
|
// Anchor left of label
|
|
} else if (anchorX < x) {
|
|
path.push('L', x, y + h / 2);
|
|
|
|
// Anchor right of label
|
|
} else if (anchorX > x + w) {
|
|
path.push('L', x + w, y + h / 2);
|
|
}
|
|
}
|
|
return path || [];
|
|
};
|
|
|
|
}(Highcharts));
|
|
}));
|