| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862 |
- /**
- * @license Highcharts JS v6.1.0 (2018-04-13)
- * 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,
- objectEach = H.objectEach,
- uniqueKey = H.uniqueKey,
- doc = H.doc,
- splat = H.splat,
- destroyObjectProperties = H.destroyObjectProperties,
- grep = H.grep,
- 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',
- * tagName: 'marker',
- * 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
- * @sample highcharts/css/annotations-markers/
- * Define markers in a styled mode
- * @since 6.0.0
- * @apioption defs
- */
- var defaultMarkers = {
- arrow: {
- tagName: 'marker',
- render: false,
- id: 'arrow',
- refY: 5,
- refX: 5,
- markerWidth: 10,
- markerHeight: 10,
- children: [{
- tagName: 'path',
- 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')
- });
- // In a styled mode definition is implemented
- H.SVGRenderer.prototype.definition = function (def) {
- var ren = this;
- function recurse(config, parent) {
- var ret;
- each(splat(config), function (item) {
- var node = ren.createElement(item.tagName),
- attr = {};
- // Set attributes
- objectEach(item, function (val, key) {
- if (
- key !== 'tagName' &&
- key !== 'children' &&
- key !== 'textContent'
- ) {
- attr[key] = val;
- }
- });
- node.attr(attr);
- // Add to the tree
- node.add(parent || ren.defs);
- // Add text content
- if (item.textContent) {
- node.element.appendChild(
- doc.createTextNode(item.textContent)
- );
- }
- // Recurse
- recurse(item.children || [], node);
- ret = node;
- });
- // Return last node added (on top level it's the only one)
- return ret;
- }
- return recurse(def);
- };
- H.SVGRenderer.prototype.addMarker = function (id, markerOptions) {
- var options = { id: id };
-
- var attrs = {
- stroke: markerOptions.color || 'none',
- fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
- };
- options.children = H.map(markerOptions.children, function (child) {
- return merge(attrs, child);
- });
-
- var marker = this.definition(merge({
- markerWidth: 20,
- markerHeight: 20,
- refX: 0,
- refY: 0,
- orient: 'auto'
- }, markerOptions, options));
- marker.id = id;
- 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
- * @private
- *
- * @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;
- if (xAxis) {
- this.plotX = xAxis.toPixels(this.x, true);
- }
- if (yAxis) {
- this.plotY = yAxis.toPixels(this.y, true);
- }
- this.isInside = this.isInsidePane();
- },
- /**
- * 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
- };
- },
- isInsidePane: function () {
- var plotX = this.plotX,
- plotY = this.plotY,
- xAxis = this.series.xAxis,
- yAxis = this.series.yAxis,
- isInside = true;
- if (xAxis) {
- isInside = defined(plotX) && plotX >= 0 && plotX <= xAxis.len;
- }
- if (yAxis) {
- isInside =
- isInside &&
- defined(plotY) &&
- plotY >= 0 && plotY <= yAxis.len;
- }
- return isInside;
- }
- };
- /* ***************************************************************************
- *
- * 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 {Chart} - the chart object
- * @param {AnnotationOptions} - the options object
- */
- var Annotation = H.Annotation = function (chart, userOptions) {
- /**
- * The chart that the annotation belongs to.
- *
- * @name chart
- * @memberOf Highcharts.Annotation#
- * @type {Chart}
- */
- this.chart = chart;
- /**
- * The array of labels which belong to the annotation.
- *
- * @name labels
- * @memberOf Highcharts.Annotation#
- * @type {Array<Highcharts.SVGElement>}
- */
- this.labels = [];
- /**
- * The array of shapes which belong to the annotation.
- *
- * @name shapes
- * @memberOf Highcharts.Annotation#
- * @type {Array<Highcharts.SVGElement>}
- */
- this.shapes = [];
- /**
- * The options for the annotations. It containers user defined options
- * merged with the default options.
- *
- * @name options
- * @memberOf Highcharts.Annotation#
- * @type {AnnotationOptions}
- */
- this.options = merge(this.defaultOptions, userOptions);
- /**
- * The callback that reports to the overlapping-labels module which
- * labels it should account for.
- *
- * @name labelCollector
- * @memberOf Highcharts.Annotation#
- * @type {Function}
- * @private
- */
- /**
- * The group element of the annotation.
- *
- * @name group
- * @memberOf Highcharts.Annotation#
- * @type {Highcharts.SVGElement}
- * @private
- */
- /**
- * The group element of the annotation's shapes.
- *
- * @name shapesGroup
- * @memberOf Highcharts.Annotation#
- * @type {Highcharts.SVGElement}
- * @private
- */
- /**
- * The group element of the annotation's labels.
- *
- * @name labelsGroup
- * @memberOf Highcharts.Annotation#
- * @type {Highcharts.SVGElement}
- * @private
- */
- this.init(chart, userOptions);
- };
- Annotation.prototype = /** @lends Highcharts.Annotation# */ {
- /**
- * Shapes which do not have background - the object is used for proper
- * setting of the contrast color
- *
- * @type {Array.<String>}
- * @private
- */
- shapesWithoutBackground: ['connector'],
- /**
- * A map object which allows to map options attributes to element
- * attributes.
- *
- * @type {Object}
- * @private
- */
- attrsMap: {
-
- backgroundColor: 'fill',
- borderColor: 'stroke',
- borderWidth: 'stroke-width',
- dashStyle: 'dashstyle',
- strokeWidth: 'stroke-width',
- stroke: 'stroke',
- fill: 'fill',
-
- zIndex: 'zIndex',
- width: 'width',
- height: 'height',
- borderRadius: 'r',
- r: 'r',
- padding: 'padding'
- },
- /**
- * Options for configuring annotations, for example labels, arrows or
- * shapes. Annotations can be tied to points, axis coordinates or chart
- * pixel coordinates.
- *
- * @private
- * @type {Array<Object>}
- * @sample highcharts/annotations/basic/
- * Basic annotations
- * @sample highcharts/demo/annotations/
- * Advanced annotations
- * @sample highcharts/css/annotations
- * Styled mode
- * @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: 3,
- /**
- * The border width in pixels for the annotation's label
- *
- * @sample highcharts/annotations/label-presentation/
- * Set labels graphic options
- */
- borderWidth: 1,
- /**
- * A class name for styling by CSS.
- *
- * @sample highcharts/css/annotations
- * Styled mode annotations
- * @since 6.0.5
- */
- className: '',
- /**
- * 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,
- /**
- * The label's pixel distance from the point.
- *
- * @type {Number}
- * @sample highcharts/annotations/label-position/
- * Set labels position
- * @default undefined
- * @apioption annotations.labelOptions.distance
- */
- /**
- * 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
- },
- /**
- * 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
- */
- /**
- * 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
- */
- /**
- * 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 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 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 Z index of the annotation.
- *
- * @type {Number}
- * @default 6
- */
- zIndex: 6
- },
- /**
- * Initialize the annotation.
- *
- * @param {Chart} - the chart
- * @param {AnnotationOptions} - the user options for the annotation
- */
- init: function () {
- var anno = this;
- each(this.options.labels || [], this.initLabel, this);
- each(this.options.shapes || [], this.initShape, this);
- this.labelCollector = function () {
- return grep(anno.labels, function (label) {
- return !label.options.allowOverlap;
- });
- };
- this.chart.labelCollectors.push(this.labelCollector);
- },
- /**
- * Main method for drawing an annotation.
- **/
- redraw: function () {
- if (!this.group) {
- this.render();
- }
- this.redrawItems(this.shapes);
- this.redrawItems(this.labels);
- },
- /**
- * @private
- * @param {Array<Object>} items
- **/
- 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]);
- }
- },
- /**
- * Render the annotation.
- **/
- 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);
- },
- /**
- * Set the annotation's visibility.
- *
- * @param {Boolean} [visibility] - Whether to show or hide an annotation.
- * If the param is omitted, the annotation's visibility is toggled.
- **/
- setVisible: function (visibility) {
- var options = this.options,
- visible = pick(visibility, !options.visible);
- this.group.attr({
- visibility: visible ? 'visible' : 'hidden'
- });
- options.visible = visible;
- },
- /**
- * Destroy the annotation. This function does not touch the chart
- * that the annotation belongs to (all annotations are kept in
- * the chart.annotations array) - it is recommended to use
- * {@link Highcharts.Chart#removeAnnotation} instead.
- **/
- destroy: function () {
- var chart = this.chart;
- erase(this.chart.labelCollectors, this.labelCollector);
- 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
- *
- * @private
- * @param {Object} shapeOptions - a confg object for a single shape
- **/
- 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);
- if (options.className) {
- shape.addClass(options.className);
- }
- this.shapes.push(shape);
- },
- /**
- * Initialisation of a single label
- *
- * @private
- * @param {Object} labelOptions
- **/
- initLabel: function (labelOptions) {
- var options = merge(this.options.labelOptions, labelOptions),
- attr = this.attrsFromOptions(options),
- label = this.chart.renderer.label(
- '',
- 0, -9e9,
- options.shape,
- null,
- null,
- options.useHTML,
- null,
- 'annotation-label'
- );
- label.points = [];
- label.options = options;
- label.itemType = 'label';
- // Labelrank required for hideOverlappingLabels()
- label.labelrank = options.labelrank;
- label.annotation = this;
- label.attr(attr);
-
- var style = options.style;
- if (style.color === 'contrast') {
- style.color = this.chart.renderer.getContrast(
- inArray(options.shape, this.shapesWithoutBackground) > -1 ?
- '#FFFFFF' :
- options.backgroundColor
- );
- }
- label.css(style).shadow(options.shadow);
-
- if (options.className) {
- label.addClass(options.className);
- }
- this.labels.push(label);
- },
- /**
- * Redrawing a single item
- *
- * @private
- * @param {SVGElement} item
- */
- redrawItem: function (item) {
- var points = this.linkPoints(item),
- itemOptions = item.options,
- text,
- time = this.chart.time;
- 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(), time) :
- itemOptions.formatter.call(points[0])
- });
- }
- if (item.type === 'path') {
- this.redrawPath(item);
- } else {
- this.alignItem(item, !item.placed);
- }
- }
- },
- /**
- * Destroing a single item
- *
- * @private
- * @param {SVGElement} item
- */
- destroyItem: function (item) {
- // erase from shapes or labels array
- erase(this[item.itemType + 's'], item);
- item.destroy();
- },
- /**
- * Returns a point object
- *
- * @private
- * @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.
- *
- * @private
- * @param {SVGElement} 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
- *
- * @private
- * @param {SVGElement} 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
- });
- }
- },
- /**
- * @private
- */
- redrawPath: function (pathItem, isNew) {
- var points = pathItem.points,
- strokeWidth = pathItem['stroke-width'] || 1,
- 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;
- },
- /*
- * @private
- */
- renderItem: function (item) {
- item.add(
- item.itemType === 'label' ?
- this.labelsGroup :
- this.shapesGroup
- );
- this.setItemMarkers(item);
- },
- /*
- * @private
- */
- setItemMarkers: function (item) {
- var itemOptions = item.options,
- chart = this.chart,
- defs = chart.options.defs,
- fill = itemOptions.fill,
- color = defined(fill) && fill !== 'none' ?
- fill :
- itemOptions.stroke,
- setMarker = function (markerType) {
- var markerId = itemOptions[markerType],
- def,
- predefinedMarker,
- key,
- marker;
- if (markerId) {
- for (key in defs) {
- def = defs[key];
- if (markerId === def.id && def.tagName === 'marker') {
- predefinedMarker = def;
- break;
- }
- }
- if (predefinedMarker) {
- marker = item[markerType] = chart.renderer.addMarker(
- (itemOptions.id || uniqueKey()) + '-' +
- predefinedMarker.id,
- merge(predefinedMarker, { color: color })
- );
- item.attr(markerType, marker.attr('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
- *
- * @private
- * @param {SVGElement} 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
- *
- * @private
- * @param {SVGElement} 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 &&
- MockPoint.prototype.isInsidePane.call(point);
- 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
- *
- * @private
- * @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.
- *
- * @private
- * @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
- *
- * @private
- * @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, /** @lends Chart# */ {
- /**
- * Add an annotation to the chart after render time.
- *
- * @param {AnnotationOptions} options
- * The series options for the new, detailed series.
- *
- * @return {Highcharts.Annotation} - The newly generated annotation.
- */
- addAnnotation: function (userOptions, redraw) {
- var annotation = new Annotation(this, userOptions);
- this.annotations.push(annotation);
- if (pick(redraw, true)) {
- annotation.redraw();
- }
- return annotation;
- },
- /**
- * Remove an annotation from the chart.
- *
- * @param {String} id - The annotation's id.
- */
- removeAnnotation: function (id) {
- var annotations = this.annotations,
- annotation = find(annotations, function (annotation) {
- return annotation.options.id === id;
- });
- if (annotation) {
- erase(annotations, annotation);
- annotation.destroy();
- }
- },
- /**
- * @private
- * @memberOf Highcharts.Chart#
- * @function drawAnnotations
- */
- 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();
- }
- });
- });
- addEvent(H.Chart, 'afterGetContainer', function () {
- this.options.defs = merge(defaultMarkers, this.options.defs || {});
-
- objectEach(this.options.defs, function (def) {
- if (def.tagName === 'marker' && def.render !== false) {
- this.renderer.addMarker(def.id, def);
- }
- }, this);
-
- });
- /* ************************************************************************* */
- /**
- * 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));
- }));
|