12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283 |
- /**
- * (c) 2009-2017 Highsoft, Black Label
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- import H from '../parts/Globals.js';
- import chartNavigationMixin from '../mixins/navigation.js';
- var doc = H.doc,
- addEvent = H.addEvent,
- pick = H.pick,
- merge = H.merge,
- extend = H.extend,
- isNumber = H.isNumber,
- fireEvent = H.fireEvent,
- isArray = H.isArray,
- isObject = H.isObject,
- objectEach = H.objectEach,
- PREFIX = 'highcharts-';
- /**
- * @private
- * @interface bindingsUtils
- */
- var bindingsUtils = {
- /**
- * Update size of background (rect) in some annotations: Measure, Simple
- * Rect.
- *
- * @private
- * @function bindingsUtils.updateRectSize
- *
- * @param {global.Event} event
- * Normalized browser event
- *
- * @param {Highcharts.Annotation} annotation
- * Annotation to be updated
- */
- updateRectSize: function (event, annotation) {
- var options = annotation.options.typeOptions,
- x = this.chart.xAxis[0].toValue(event.chartX),
- y = this.chart.yAxis[0].toValue(event.chartY),
- width = x - options.point.x,
- height = options.point.y - y;
- annotation.update({
- typeOptions: {
- background: {
- width: width,
- height: height
- }
- }
- });
- },
- /**
- * Get field type according to value
- *
- * @private
- * @function bindingsUtils.getFieldType
- *
- * @param {*} value
- * Atomic type (one of: string, number, boolean)
- *
- * @return {string}
- * Field type (one of: text, number, checkbox)
- */
- getFieldType: function (value) {
- return {
- 'string': 'text',
- 'number': 'number',
- 'boolean': 'checkbox'
- }[typeof value];
- }
- };
- H.NavigationBindings = function (chart, options) {
- this.chart = chart;
- this.options = options;
- this.eventsToUnbind = [];
- this.container = doc.getElementsByClassName(
- this.options.bindingsClassName
- );
- };
- // Define which options from annotations should show up in edit box:
- H.NavigationBindings.annotationsEditable = {
- // `typeOptions` are always available
- // Nested and shared options:
- nestedOptions: {
- labelOptions: ['style', 'format', 'backgroundColor'],
- labels: ['style'],
- label: ['style'],
- style: ['fontSize', 'color'],
- background: ['fill', 'strokeWidth', 'stroke'],
- innerBackground: ['fill', 'strokeWidth', 'stroke'],
- outerBackground: ['fill', 'strokeWidth', 'stroke'],
- shapeOptions: ['fill', 'strokeWidth', 'stroke'],
- shapes: ['fill', 'strokeWidth', 'stroke'],
- line: ['strokeWidth', 'stroke'],
- backgroundColors: [true],
- connector: ['fill', 'strokeWidth', 'stroke'],
- crosshairX: ['strokeWidth', 'stroke'],
- crosshairY: ['strokeWidth', 'stroke']
- },
- // Simple shapes:
- circle: ['shapes'],
- verticalLine: [],
- label: ['labelOptions'],
- // Measure
- measure: ['background', 'crosshairY', 'crosshairX'],
- // Others:
- fibonacci: [],
- tunnel: ['background', 'line', 'height'],
- pitchfork: ['innerBackground', 'outerBackground'],
- rect: ['shapes'],
- // Crooked lines, elliots, arrows etc:
- crookedLine: []
- };
- // Define non editable fields per annotation, for example Rectangle inherits
- // options from Measure, but crosshairs are not available
- H.NavigationBindings.annotationsNonEditable = {
- rectangle: ['crosshairX', 'crosshairY', 'label']
- };
- extend(H.NavigationBindings.prototype, {
- // Private properties added by bindings:
- // Active (selected) annotation that is editted through popup/forms
- // activeAnnotation: Annotation
- // Holder for current step, used on mouse move to update bound object
- // mouseMoveEvent: function () {}
- // Next event in `step` array to be called on chart's click
- // nextEvent: function () {}
- // Index in the `step` array of the current event
- // stepIndex: 0
- // Flag to determine if current binding has steps
- // steps: true|false
- // Bindings holder for all events
- // selectedButton: {}
- // Holder for user options, returned from `start` event, and passed on to
- // `step`'s' and `end`.
- // currentUserDetails: {}
- /**
- * Initi all events conencted to NavigationBindings.
- *
- * @private
- * @function Highcharts.NavigationBindings#initEvents
- */
- initEvents: function () {
- var navigation = this,
- chart = navigation.chart,
- bindingsContainer = navigation.container,
- options = navigation.options;
- // Shorthand object for getting events for buttons:
- navigation.boundClassNames = {};
- objectEach(options.bindings, function (value) {
- navigation.boundClassNames[value.className] = value;
- });
- // Handle multiple containers with the same class names:
- [].forEach.call(bindingsContainer, function (subContainer) {
- navigation.eventsToUnbind.push(
- addEvent(
- subContainer,
- 'click',
- function (event) {
- var bindings = navigation.getButtonEvents(
- bindingsContainer,
- event
- );
- if (bindings) {
- navigation.bindingsButtonClick(
- bindings.button,
- bindings.events,
- event
- );
- }
- }
- )
- );
- });
- objectEach(options.events || {}, function (callback, eventName) {
- navigation.eventsToUnbind.push(
- addEvent(
- navigation,
- eventName,
- callback
- )
- );
- });
- navigation.eventsToUnbind.push(
- addEvent(chart.container, 'click', function (e) {
- if (
- !chart.cancelClick &&
- chart.isInsidePlot(
- e.chartX - chart.plotLeft,
- e.chartY - chart.plotTop
- )
- ) {
- navigation.bindingsChartClick(this, e);
- }
- })
- );
- navigation.eventsToUnbind.push(
- addEvent(chart.container, 'mousemove', function (e) {
- navigation.bindingsContainerMouseMove(this, e);
- })
- );
- },
- /**
- * Common chart.update() delegation, shared between bindings and exporting.
- *
- * @private
- * @function Highcharts.NavigationBindings#initUpdate
- */
- initUpdate: function () {
- var navigation = this;
- chartNavigationMixin.addUpdate(
- function (options) {
- navigation.update(options);
- },
- this.chart
- );
- },
- /**
- * Hook for click on a button, method selcts/unselects buttons,
- * then calls `bindings.init` callback.
- *
- * @private
- * @function Highcharts.NavigationBindings#bindingsButtonClick
- *
- * @param {Highcharts.HTMLDOMElement} [button]
- * Clicked button
- *
- * @param {object} [events]
- * Events passed down from bindings (`init`, `start`, `step`, `end`)
- *
- * @param {global.Event} [clickEvent]
- * Browser's click event
- */
- bindingsButtonClick: function (button, events, clickEvent) {
- var navigation = this,
- chart = navigation.chart;
- if (navigation.selectedButtonElement) {
- fireEvent(
- navigation,
- 'deselectButton',
- { button: navigation.selectedButtonElement }
- );
- if (navigation.nextEvent) {
- // Remove in-progress annotations adders:
- if (
- navigation.currentUserDetails &&
- navigation.currentUserDetails.coll === 'annotations'
- ) {
- chart.removeAnnotation(navigation.currentUserDetails);
- }
- navigation.mouseMoveEvent = navigation.nextEvent = false;
- }
- }
- navigation.selectedButton = events;
- navigation.selectedButtonElement = button;
- fireEvent(navigation, 'selectButton', { button: button });
- // Call "init" event, for example to open modal window
- if (events.init) {
- events.init.call(navigation, button, clickEvent);
- }
- if (events.start || events.steps) {
- chart.renderer.boxWrapper.addClass(PREFIX + 'draw-mode');
- }
- },
- /**
- * Hook for click on a chart, first click on a chart calls `start` event,
- * then on all subsequent clicks iterate over `steps` array.
- * When finished, calls `end` event.
- *
- * @private
- * @function Highcharts.NavigationBindings#bindingsChartClick
- *
- * @param {Highcharts.Chart} chart
- * Chart that click was performed on.
- *
- * @param {global.Event} clickEvent
- * Browser's click event.
- */
- bindingsChartClick: function (chartContainer, clickEvent) {
- var navigation = this,
- chart = navigation.chart,
- selectedButton = navigation.selectedButton,
- svgContainer = chart.renderer.boxWrapper;
- if (
- navigation.activeAnnotation &&
- !clickEvent.activeAnnotation &&
- // Element could be removed in the child action, e.g. button
- clickEvent.target.parentNode &&
- // TO DO: Polyfill for IE11?
- !clickEvent.target.closest('.' + PREFIX + 'popup')
- ) {
- fireEvent(navigation, 'closePopup');
- navigation.deselectAnnotation();
- }
- if (!selectedButton || !selectedButton.start) {
- return;
- }
- if (!navigation.nextEvent) {
- // Call init method:
- navigation.currentUserDetails = selectedButton.start.call(
- navigation,
- clickEvent
- );
- // If steps exists (e.g. Annotations), bind them:
- if (selectedButton.steps) {
- navigation.stepIndex = 0;
- navigation.steps = true;
- navigation.mouseMoveEvent = navigation.nextEvent =
- selectedButton.steps[navigation.stepIndex];
- } else {
- fireEvent(
- navigation,
- 'deselectButton',
- { button: navigation.selectedButtonElement }
- );
- svgContainer.removeClass(PREFIX + 'draw-mode');
- navigation.steps = false;
- navigation.selectedButton = null;
- // First click is also the last one:
- if (selectedButton.end) {
- selectedButton.end.call(
- navigation,
- clickEvent,
- navigation.currentUserDetails
- );
- }
- }
- } else {
- navigation.nextEvent(
- clickEvent,
- navigation.currentUserDetails
- );
- if (navigation.steps) {
- navigation.stepIndex++;
- if (selectedButton.steps[navigation.stepIndex]) {
- // If we have more steps, bind them one by one:
- navigation.mouseMoveEvent = navigation.nextEvent =
- selectedButton.steps[navigation.stepIndex];
- } else {
- fireEvent(
- navigation,
- 'deselectButton',
- { button: navigation.selectedButtonElement }
- );
- svgContainer.removeClass(PREFIX + 'draw-mode');
- // That was the last step, call end():
- if (selectedButton.end) {
- selectedButton.end.call(
- navigation,
- clickEvent,
- navigation.currentUserDetails
- );
- }
- navigation.nextEvent = false;
- navigation.mouseMoveEvent = false;
- navigation.selectedButton = null;
- }
- }
- }
- },
- /**
- * Hook for mouse move on a chart's container. It calls current step.
- *
- * @private
- * @function Highcharts.NavigationBindings#bindingsContainerMouseMove
- *
- * @param {Highcharts.HTMLDOMElement} container
- * Chart's container.
- *
- * @param {global.Event} moveEvent
- * Browser's move event.
- */
- bindingsContainerMouseMove: function (container, moveEvent) {
- if (this.mouseMoveEvent) {
- this.mouseMoveEvent(
- moveEvent,
- this.currentUserDetails
- );
- }
- },
- /**
- * Translate fields (e.g. `params.period` or `marker.styles.color`) to
- * Highcharts options object (e.g. `{ params: { period } }`).
- *
- * @private
- * @function Highcharts.NavigationBindings#fieldsToOptions
- *
- * @param {object} fields
- * Fields from popup form.
- *
- * @param {object} config
- * Default config to be modified.
- *
- * @return {object}
- * Modified config
- */
- fieldsToOptions: function (fields, config) {
- objectEach(fields, function (value, field) {
- var parsedValue = parseFloat(value),
- path = field.split('.'),
- parent = config,
- pathLength = path.length - 1;
- // If it's a number (not "forma" options), parse it:
- if (
- isNumber(parsedValue) &&
- !value.match(/px/g) &&
- !field.match(/format/g)
- ) {
- value = parsedValue;
- }
- // Remove empty strings or values like 0
- if (value !== '' && value !== 'undefined') {
- path.forEach(function (name, index) {
- var nextName = pick(path[index + 1], '');
- if (pathLength === index) {
- // Last index, put value:
- parent[name] = value;
- } else if (!parent[name]) {
- // Create middle property:
- parent[name] = nextName.match(/\d/g) ? [] : {};
- parent = parent[name];
- } else {
- // Jump into next property
- parent = parent[name];
- }
- });
- }
- });
- return config;
- },
- /**
- * Shorthand method to deselect an annotation.
- *
- * @function Highcharts.NavigationBindings#deselectAnnotation
- */
- deselectAnnotation: function () {
- if (this.activeAnnotation) {
- this.activeAnnotation.setControlPointsVisibility(false);
- this.activeAnnotation = false;
- }
- },
- /**
- * Generates API config for popup in the same format as options for
- * Annotation object.
- *
- * @function Highcharts.NavigationBindings#annotationToFields
- *
- * @param {Highcharts.Annotation} annotation
- * Annotations object
- *
- * @return {object}
- * Annotation options to be displayed in popup box
- */
- annotationToFields: function (annotation) {
- var options = annotation.options,
- editables = H.NavigationBindings.annotationsEditable,
- nestedEditables = editables.nestedOptions,
- getFieldType = this.utils.getFieldType,
- type = pick(
- options.type,
- options.shapes && options.shapes[0] &&
- options.shapes[0].type,
- options.labels && options.labels[0] &&
- options.labels[0].itemType,
- 'label'
- ),
- nonEditables = H.NavigationBindings
- .annotationsNonEditable[options.langKey] || [],
- visualOptions = {
- langKey: options.langKey,
- type: type
- };
- /**
- * Nested options traversing. Method goes down to the options and copies
- * allowed options (with values) to new object, which is last parameter:
- * "parent".
- *
- * @private
- * @function Highcharts.NavigationBindings#annotationToFields.traverse
- *
- * @param {*} option
- * Atomic type or object/array
- *
- * @param {string} key
- * Option name, for example "visible" or "x", "y"
- *
- * @param {object} allowed
- * Editables from H.NavigationBindings.annotationsEditable
- *
- * @param {object} parent
- * Where new options will be assigned
- */
- function traverse(option, key, parentEditables, parent) {
- var nextParent;
- if (
- parentEditables &&
- nonEditables.indexOf(key) === -1 &&
- (
- (
- parentEditables.indexOf &&
- parentEditables.indexOf(key)
- ) >= 0 ||
- parentEditables[key] || // nested array
- parentEditables === true // simple array
- )
- ) {
- // Roots:
- if (isArray(option)) {
- parent[key] = [];
- option.forEach(function (arrayOption, i) {
- if (!isObject(arrayOption)) {
- // Simple arrays, e.g. [String, Number, Boolean]
- traverse(
- arrayOption,
- 0,
- nestedEditables[key],
- parent[key]
- );
- } else {
- // Advanced arrays, e.g. [Object, Object]
- parent[key][i] = {};
- objectEach(
- arrayOption,
- function (nestedOption, nestedKey) {
- traverse(
- nestedOption,
- nestedKey,
- nestedEditables[key],
- parent[key][i]
- );
- }
- );
- }
- });
- } else if (isObject(option)) {
- nextParent = {};
- if (isArray(parent)) {
- parent.push(nextParent);
- nextParent[key] = {};
- nextParent = nextParent[key];
- } else {
- parent[key] = nextParent;
- }
- objectEach(option, function (nestedOption, nestedKey) {
- traverse(
- nestedOption,
- nestedKey,
- key === 0 ? parentEditables : nestedEditables[key],
- nextParent
- );
- });
- } else {
- // Leaf:
- if (key === 'format') {
- parent[key] = [
- H.format(
- option,
- annotation.labels[0].points[0]
- ).toString(),
- 'text'
- ];
- } else if (isArray(parent)) {
- parent.push([option, getFieldType(option)]);
- } else {
- parent[key] = [option, getFieldType(option)];
- }
- }
- }
- }
- objectEach(options, function (option, key) {
- if (key === 'typeOptions') {
- visualOptions[key] = {};
- objectEach(options[key], function (typeOption, typeKey) {
- traverse(
- typeOption,
- typeKey,
- nestedEditables,
- visualOptions[key],
- true
- );
- });
- } else {
- traverse(option, key, editables[type], visualOptions);
- }
- });
- return visualOptions;
- },
- /**
- * Get all class names for all parents in the element. Iterates until finds
- * main container.
- *
- * @function Highcharts.NavigationBindings#getClickedClassNames
- *
- * @param {Highcharts.HTMLDOMElement}
- * Container that event is bound to.
- *
- * @param {global.Event} event
- * Browser's event.
- *
- * @return {Array<string>}
- * Array of class names with corresponding elements
- */
- getClickedClassNames: function (container, event) {
- var element = event.target,
- classNames = [],
- elemClassName;
- while (element) {
- elemClassName = H.attr(element, 'class');
- if (elemClassName) {
- classNames = classNames.concat(
- elemClassName.split(' ').map(
- function (name) { // eslint-disable-line no-loop-func
- return [
- name,
- element
- ];
- }
- )
- );
- }
- element = element.parentNode;
- if (element === container) {
- return classNames;
- }
- }
- return classNames;
- },
- /**
- * Get events bound to a button. It's a custom event delegation to find all
- * events connected to the element.
- *
- * @function Highcharts.NavigationBindings#getButtonEvents
- *
- * @param {Highcharts.HTMLDOMElement}
- * Container that event is bound to.
- *
- * @param {global.Event} event
- * Browser's event.
- *
- * @return {object}
- * Oject with events (init, start, steps, and end)
- */
- getButtonEvents: function (container, event) {
- var navigation = this,
- classNames = this.getClickedClassNames(container, event),
- bindings;
- classNames.forEach(function (className) {
- if (navigation.boundClassNames[className[0]] && !bindings) {
- bindings = {
- events: navigation.boundClassNames[className[0]],
- button: className[1]
- };
- }
- });
- return bindings;
- },
- /**
- * Bindings are just events, so the whole update process is simply
- * removing old events and adding new ones.
- *
- * @private
- * @function Highcharts.NavigationBindings#update
- */
- update: function (options) {
- this.options = merge(true, this.options, options);
- this.removeEvents();
- this.initEvents();
- },
- /**
- * Remove all events created in the navigation.
- *
- * @private
- * @function Highcharts.NavigationBindings#removeEvents
- */
- removeEvents: function () {
- this.eventsToUnbind.forEach(function (unbinder) {
- unbinder();
- });
- },
- destroy: function () {
- this.removeEvents();
- },
- /**
- * General utils for bindings
- *
- * @private
- * @name Highcharts.NavigationBindings#utils
- * @type {bindingsUtils}
- */
- utils: bindingsUtils
- });
- H.Chart.prototype.initNavigationBindings = function () {
- var chart = this,
- options = chart.options;
- if (options && options.navigation && options.navigation.bindings) {
- chart.navigationBindings = new H.NavigationBindings(
- chart,
- options.navigation
- );
- chart.navigationBindings.initEvents();
- chart.navigationBindings.initUpdate();
- }
- };
- addEvent(H.Chart, 'load', function () {
- this.initNavigationBindings();
- });
- addEvent(H.Chart, 'destroy', function () {
- if (this.navigationBindings) {
- this.navigationBindings.destroy();
- }
- });
- addEvent(H.NavigationBindings, 'deselectButton', function () {
- this.selectedButtonElement = null;
- });
- // Show edit-annotation form:
- function selectableAnnotation(annotationType) {
- var originalClick = annotationType.prototype.defaultOptions.events &&
- annotationType.prototype.defaultOptions.events.click;
- function selectAndshowPopup(event) {
- var annotation = this,
- navigation = annotation.chart.navigationBindings,
- prevAnnotation = navigation.activeAnnotation;
- if (originalClick) {
- originalClick.click.call(annotation, event);
- }
- if (prevAnnotation !== annotation) {
- // Select current:
- navigation.deselectAnnotation();
- navigation.activeAnnotation = annotation;
- annotation.setControlPointsVisibility(true);
- fireEvent(
- navigation,
- 'showPopup',
- {
- annotation: annotation,
- formType: 'annotation-toolbar',
- options: navigation.annotationToFields(annotation),
- onSubmit: function (data) {
- var config = {},
- typeOptions;
- if (data.actionType === 'remove') {
- navigation.activeAnnotation = false;
- navigation.chart.removeAnnotation(annotation);
- } else {
- navigation.fieldsToOptions(data.fields, config);
- navigation.deselectAnnotation();
- typeOptions = config.typeOptions;
- if (annotation.options.type === 'measure') {
- // Manually disable crooshars according to
- // stroke width of the shape:
- typeOptions.crosshairY.enabled =
- typeOptions.crosshairY.strokeWidth !== 0;
- typeOptions.crosshairX.enabled =
- typeOptions.crosshairX.strokeWidth !== 0;
- }
- annotation.update(config);
- }
- }
- }
- );
- } else {
- // Deselect current:
- navigation.deselectAnnotation();
- fireEvent(navigation, 'closePopup');
- }
- // Let bubble event to chart.click:
- event.activeAnnotation = true;
- }
- H.merge(
- true,
- annotationType.prototype.defaultOptions.events,
- {
- click: selectAndshowPopup
- }
- );
- }
- if (H.Annotation) {
- // Basic shapes:
- selectableAnnotation(H.Annotation);
- // Advanced annotations:
- H.objectEach(H.Annotation.types, function (annotationType) {
- selectableAnnotation(annotationType);
- });
- }
- H.setOptions({
- /**
- * @optionparent lang
- */
- lang: {
- /**
- * Configure the Popup strings in the chart. Requires the
- * `annotations.js` or `annotations-advanced.src.js` module to be
- * loaded.
- *
- * @since 7.0.0
- * @type {Object}
- * @product highcharts highstock
- */
- navigation: {
- /**
- * Translations for all field names used in popup.
- *
- * @product highcharts highstock
- * @type {Object}
- */
- popup: {
- simpleShapes: 'Simple shapes',
- lines: 'Lines',
- circle: 'Circle',
- rectangle: 'Rectangle',
- label: 'Label',
- shapeOptions: 'Shape options',
- typeOptions: 'Details',
- fill: 'Fill',
- format: 'Text',
- strokeWidth: 'Line width',
- stroke: 'Line color',
- title: 'Title',
- name: 'Name',
- labelOptions: 'Label options',
- labels: 'Labels',
- backgroundColor: 'Background color',
- backgroundColors: 'Background colors',
- borderColor: 'Border color',
- borderRadius: 'Border radius',
- borderWidth: 'Border width',
- style: 'Style',
- padding: 'Padding',
- fontSize: 'Font size',
- color: 'Color',
- height: 'Height',
- shapes: 'Shape options'
- }
- }
- },
- /**
- * @optionparent navigation
- * @product highcharts highstock
- */
- navigation: {
- /**
- * A CSS class name where all bindings will be attached to. Multiple
- * charts on the same page should have separate class names to prevent
- * duplicating events.
- *
- * @since 7.0.0
- * @type {string}
- */
- bindingsClassName: 'highcharts-bindings-wrapper',
- /**
- * Bindings definitions for custom HTML buttons. Each binding implements
- * simple event-driven interface:
- *
- * - `className`: classname used to bind event to
- *
- * - `init`: initial event, fired on button click
- *
- * - `start`: fired on first click on a chart
- *
- * - `steps`: array of sequential events fired one after another on each
- * of users clicks
- *
- * - `end`: last event to be called after last step event
- *
- * @type {Highcharts.Dictionary<Highcharts.StockToolsBindingsObject>|*}
- * @sample stock/stocktools/stocktools-thresholds
- * Custom bindings in Highstock
- * @since 7.0.0
- * @product highcharts highstock
- */
- bindings: {
- /**
- * A circle annotation bindings. Includes `start` and one event in
- * `steps` array.
- *
- * @type {Highcharts.StockToolsBindingsObject}
- * @default {"className": "highcharts-circle-annotation", "start": function() {}, "steps": [function() {}]}
- */
- circleAnnotation: {
- /** @ignore */
- className: 'highcharts-circle-annotation',
- /** @ignore */
- start: function (e) {
- var x = this.chart.xAxis[0].toValue(e.chartX),
- y = this.chart.yAxis[0].toValue(e.chartY),
- annotation;
- annotation = this.chart.addAnnotation({
- langKey: 'circle',
- shapes: [{
- type: 'circle',
- point: {
- xAxis: 0,
- yAxis: 0,
- x: x,
- y: y
- },
- r: 5,
- controlPoints: [{
- positioner: function (target) {
- var xy = H.Annotation.MockPoint
- .pointToPixels(
- target.points[0]
- ),
- r = target.options.r;
- return {
- x: xy.x + r * Math.cos(Math.PI / 4) -
- this.graphic.width / 2,
- y: xy.y + r * Math.sin(Math.PI / 4) -
- this.graphic.height / 2
- };
- },
- events: {
- // TRANSFORM RADIUS ACCORDING TO Y
- // TRANSLATION
- drag: function (e, target) {
- var annotation = target.annotation,
- position = this
- .mouseMoveToTranslation(e);
- target.setRadius(
- Math.max(
- target.options.r +
- position.y /
- Math.sin(Math.PI / 4),
- 5
- )
- );
- annotation.options.shapes[0] =
- annotation.userOptions.shapes[0] =
- target.options;
- target.redraw(false);
- }
- }
- }]
- }]
- });
- return annotation;
- },
- /** @ignore */
- steps: [
- function (e, annotation) {
- var point = annotation.options.shapes[0].point,
- x = this.chart.xAxis[0].toPixels(point.x),
- y = this.chart.yAxis[0].toPixels(point.y),
- distance = Math.max(
- Math.sqrt(
- Math.pow(x - e.chartX, 2) +
- Math.pow(y - e.chartY, 2)
- ),
- 5
- );
- annotation.update({
- shapes: [{
- r: distance
- }]
- });
- }
- ]
- },
- /**
- * A rectangle annotation bindings. Includes `start` and one event
- * in `steps` array.
- *
- * @type {Highcharts.StockToolsBindingsObject}
- * @default {"className": "highcharts-rectangle-annotation", "start": function() {}, "steps": [function() {}]}
- */
- rectangleAnnotation: {
- /** @ignore */
- className: 'highcharts-rectangle-annotation',
- /** @ignore */
- start: function (e) {
- var x = this.chart.xAxis[0].toValue(e.chartX),
- y = this.chart.yAxis[0].toValue(e.chartY),
- options = {
- langKey: 'rectangle',
- shapes: [{
- type: 'rect',
- point: {
- x: x,
- y: y,
- xAxis: 0,
- yAxis: 0
- },
- width: 5,
- height: 5,
- controlPoints: [{
- positioner: function (target) {
- var xy = H.Annotation.MockPoint
- .pointToPixels(
- target.points[0]
- );
- return {
- x: xy.x + target.options.width - 4,
- y: xy.y + target.options.height - 4
- };
- },
- events: {
- drag: function (e, target) {
- var annotation = target.annotation,
- xy = this
- .mouseMoveToTranslation(e);
- target.options.width = Math.max(
- target.options.width + xy.x,
- 5
- );
- target.options.height = Math.max(
- target.options.height + xy.y,
- 5
- );
- annotation.options.shapes[0] =
- target.options;
- annotation.userOptions.shapes[0] =
- target.options;
- target.redraw(false);
- }
- }
- }]
- }]
- };
- return this.chart.addAnnotation(options);
- },
- /** @ignore */
- steps: [
- function (e, annotation) {
- var xAxis = this.chart.xAxis[0],
- yAxis = this.chart.yAxis[0],
- point = annotation.options.shapes[0].point,
- x = xAxis.toPixels(point.x),
- y = yAxis.toPixels(point.y),
- width = Math.max(e.chartX - x, 5),
- height = Math.max(e.chartY - y, 5);
- annotation.update({
- shapes: [{
- width: width,
- height: height,
- point: {
- x: point.x,
- y: point.y
- }
- }]
- });
- }
- ]
- },
- /**
- * A label annotation bindings. Includes `start` event only.
- *
- * @type {Highcharts.StockToolsBindingsObject}
- * @default {"className": "highcharts-label-annotation", "start": function() {}, "steps": [function() {}]}
- */
- labelAnnotation: {
- /** @ignore */
- className: 'highcharts-label-annotation',
- /** @ignore */
- start: function (e) {
- var x = this.chart.xAxis[0].toValue(e.chartX),
- y = this.chart.yAxis[0].toValue(e.chartY);
- this.chart.addAnnotation({
- langKey: 'label',
- labelOptions: {
- format: '{y:.2f}'
- },
- labels: [{
- point: {
- x: x,
- y: y,
- xAxis: 0,
- yAxis: 0
- },
- controlPoints: [{
- symbol: 'triangle-down',
- positioner: function (target) {
- if (!target.graphic.placed) {
- return {
- x: 0,
- y: -9e7
- };
- }
- var xy = H.Annotation.MockPoint
- .pointToPixels(
- target.points[0]
- );
- return {
- x: xy.x - this.graphic.width / 2,
- y: xy.y - this.graphic.height / 2
- };
- },
- // TRANSLATE POINT/ANCHOR
- events: {
- drag: function (e, target) {
- var xy = this.mouseMoveToTranslation(e);
- target.translatePoint(xy.x, xy.y);
- target.annotation.labels[0].options =
- target.options;
- target.redraw(false);
- }
- }
- }, {
- symbol: 'square',
- positioner: function (target) {
- if (!target.graphic.placed) {
- return {
- x: 0,
- y: -9e7
- };
- }
- return {
- x: target.graphic.alignAttr.x -
- this.graphic.width / 2,
- y: target.graphic.alignAttr.y -
- this.graphic.height / 2
- };
- },
- // TRANSLATE POSITION WITHOUT CHANGING THE
- // ANCHOR
- events: {
- drag: function (e, target) {
- var xy = this.mouseMoveToTranslation(e);
- target.translate(xy.x, xy.y);
- target.annotation.labels[0].options =
- target.options;
- target.redraw(false);
- }
- }
- }],
- overflow: 'none',
- crop: true
- }]
- });
- }
- }
- },
- /**
- * A `showPopup` event. Fired when selecting for example an annotation.
- *
- * @type {Function}
- * @apioption navigation.events.showPopup
- */
- /**
- * A `hidePopop` event. Fired when Popup should be hidden, for exampole
- * when clicking on an annotation again.
- *
- * @type {Function}
- * @apioption navigation.events.hidePopup
- */
- /**
- * Event fired on a button click.
- *
- * @type {Function}
- * @sample highcharts/annotations/gui/
- * Change icon in a dropddown on event
- * @sample highcharts/annotations/gui-buttons/
- * Change button class on event
- * @apioption navigation.events.selectButton
- */
- /**
- * Event fired when button state should change, for example after
- * adding an annotation.
- *
- * @type {Function}
- * @sample highcharts/annotations/gui/
- * Change icon in a dropddown on event
- * @sample highcharts/annotations/gui-buttons/
- * Change button class on event
- * @apioption navigation.events.deselectButton
- */
- /**
- * Events to communicate between Stock Tools and custom GUI.
- *
- * @since 7.0.0
- * @product highcharts highstock
- * @optionparent navigation.events
- */
- events: {}
- }
- });
|