UNPKG

flot-cursors-plugin

Version:
1,571 lines 64.4 kB
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

/* Flot plugin for adding cursors to the plot.

Copyright (c) cipix2000@gmail.com.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
*/

/*global jQuery*/

(function ($) {
    'use strict';

    var options = {
        cursors: []
    };

    var constants = {
        iRectSize: 8,
        mouseGrabMargin: 12,
        textHeight: 10, // to do: compute it somehow. Canvas doesn't give us a way to know it
        labelPadding: 10,
        thumbRadius: 17
    };

    var positionModes = Object.freeze({
        axis: 0,
        relative: 1
    });

    var snapModes = Object.freeze({
        xy: 0, // when dragging the cursor, snap to the nearest point on the plane
        x: 1 // on a chart update, snap to the nearest point on the x axis
    });

    var showThumbToShapeMap = Object.freeze({
        t: 'top',
        b: 'bottom',
        l: 'left',
        r: 'right'
    });

    function init(plot) {
        var cursors = [];
        var update = [];
        var positionMode = positionModes.axis;
        var snapMode = snapModes.xy;
        var thumbPreviousPositionMap = new Map();
        var plotPositionConstrain = void 0;
        var svgRoot = void 0;

        function createCursor(options) {
            var defaultCursor = createDefaultCursor();
            processCursorOptions(defaultCursor, options);
            return mixin(options, defaultCursor);
        }

        function createDefaultCursor() {
            return {
                name: 'unnamed ' + cursors.length,
                position: {
                    relativeX: 0.5, // relative to the width of the drawing area
                    relativeY: 0.5, // relative to the height of the drawing area
                    x: undefined, // plot value
                    y: undefined // plot value
                },
                mousePosition: {
                    relativeX: undefined,
                    relativeY: undefined
                },
                x: 0, // canvas point relative to the actual data area
                y: 0, // canvas point relative to the actual data area
                valign: 'below',
                halign: 'right',
                show: true,
                selected: false,
                highlighted: false,
                mode: 'xy',
                showIntersections: false,
                showLabel: false,
                showValues: true,
                color: 'gray',
                fontSize: '10px',
                fontFamily: 'sans-serif',
                fontStyle: '',
                fontWeight: '',
                lineWidth: 1,
                movable: true,
                dashes: [],
                intersectionColor: 'darkgray',
                intersectionLabelPosition: 'bottom-right',
                snapToPlot: undefined,
                interpolate: false,
                defaultxaxis: 1,
                defaultyaxis: 1,
                symbolSize: 8,
                showThumbs: 'none',
                thumbAbbreviation: 'C' + cursors.length,
                thumbClassList: ['draggable'],
                thumbs: [],
                thumbColor: undefined,
                horizontalThumbConstrain: undefined,
                verticalThumbConstrain: undefined
            };
        }

        plot.hooks.processOptions.push(function (plot) {
            plot.getOptions().cursors.forEach(function (options) {
                plot.addCursor(options);
            });
        });

        plot.hooks.processOffset.push(function (plot, offset) {
            reserveSpaceForThumbs(cursors, plot, offset);
        });

        plot.getCursors = function () {
            return cursors;
        };

        plot.addCursor = function addCursor(options) {
            var currentCursor = createCursor(options);

            setPosition(plot, currentCursor, currentCursor.position, positionMode);

            cursors.push(currentCursor);

            plot.triggerRedrawOverlay();
        };

        plot.removeCursor = function removeCursor(cursor) {
            var index = cursors.indexOf(cursor);

            if (index !== -1) {
                // remove thumbs
                cursor.thumbs.forEach(function (thumb) {
                    dispatchThumbEvent('thumbWillBeRemoved', { current: thumb });
                    svgRoot.removeChild(thumb);
                    thumbPreviousPositionMap.delete(thumb);
                });
                cursors.splice(index, 1);
            }

            plot.triggerRedrawOverlay();
        };

        plot.setCursor = function setCursor(cursor, options) {
            var index = cursors.indexOf(cursor);

            if (index !== -1) {
                processCursorOptions(cursor, options);
                mixin(options, cursors[index]);
                setPosition(plot, cursors[index], cursors[index].position);
                if (cursor._createThumbs) {
                    plot.setupGrid();
                    plot.draw();
                } else {
                    plot.triggerRedrawOverlay();
                }
            }
        };

        plot.getIntersections = function getIntersections(cursor) {
            var index = cursors.indexOf(cursor);

            if (index !== -1) {
                return cursors[index].intersections;
            }

            return [];
        };

        plot.formatCursorPosition = formatCursorPosition;

        plot.setPlotPositionConstrain = function (constrain) {
            if (typeof constrain !== 'function') {
                throw new Error('plot position constrain should be a function');
            }
            plotPositionConstrain = constrain;
        };

        function processCursorOptions(cursor, options) {
            processModeAndThumbs(cursor, options);
            processVisibilityOptions(cursor, options);
        }

        function shouldCoerceWhenModeAndShowThumbsDoNotMatch(mode, showThumbs) {
            if (mode === 'x' && showThumbs.search(/[lr]/) !== -1) {
                // a vertical cursor can't have left or right thumb
                return true;
            }
            if (mode === 'y' && showThumbs.search(/[tb]/) !== -1) {
                // a horizontal cursor can't have top or bottom thumb
                return true;
            }
            if (mode === 'xy' && showThumbs.search(/l\s*r|r\s*l|t\s*b|b\s*t/) !== -1) {
                // xy cursor can't have both left and right thumb or top and bottom thumb
                return true;
            }
            return false;
        }

        function validateAndCoerceModeAndShowThumbs(cursor, options) {
            if (options.mode && options.showThumbs) {
                if (shouldCoerceWhenModeAndShowThumbsDoNotMatch(options.mode, options.showThumbs)) {
                    console.log('showThumbs ' + options.showThumbs + ' doesn\'t match mode ' + options.mode + '.');
                    switch (options.mode) {
                        case 'x':
                            options.showThumbs = 't';
                            break;
                        case 'y':
                            options.showThumbs = 'r';
                            break;
                        case 'xy':
                            options.showThumbs = 'tr';
                            break;
                    }
                    console.log('showThumbs is coerced to ' + options.showThumbs);
                }
            } else if (options.mode) {
                if (shouldCoerceWhenModeAndShowThumbsDoNotMatch(options.mode, cursor.showThumbs)) {
                    console.log('mode doesn\'t match the existing showThumbs. It will be ignored');
                    delete options.mode;
                }
            } else if (options.showThumbs) {
                if (shouldCoerceWhenModeAndShowThumbsDoNotMatch(cursor.mode, options.showThumbs)) {
                    console.log('showThumbs doesn\'t match the existing mode. It will be ignored');
                    delete options.showThumbs;
                }
            }
        }

        function processModeAndThumbs(cursor, options) {
            if (options.showThumbs === 'none') {
                return;
            }

            validateAndCoerceModeAndShowThumbs(cursor, options);

            if (!options.showThumbs) {
                // no showThumbs in options
                return;
            }

            var newShapes = options.showThumbs.split('').map(function (location) {
                return showThumbToShapeMap[location];
            });
            var thumbToRemove = void 0;
            for (var i = 0; i < cursor.thumbs.length; i++) {
                // remove old thumb and new thumbs will be created in the draw overlay phase
                if (!newShapes.includes(cursor.thumbs[i].shape)) {
                    thumbToRemove = cursor.thumbs[i];
                    cursor.thumbs[i] = null;
                    cursor._createThumbs = true;
                    dispatchThumbEvent('thumbWillBeRemoved', { current: thumbToRemove });
                    svgRoot.removeChild(thumbToRemove);
                    thumbPreviousPositionMap.delete(thumbToRemove);
                }
            }
        }

        function processVisibilityOptions(cursor, options) {
            if (cursor.thumbs.length === 0) {
                return;
            }

            var finalShow = options.show !== undefined ? options.show : cursor.show;
            var finalShowThumbs = options.showThumbs !== undefined ? options.showThumbs : cursor.showThumbs;
            var showChanged = options.show !== undefined ? options.show !== cursor.show : false;
            var showThumbsMakesVisibilityChange = options.showThumbs !== undefined ? options.showThumbs !== cursor.showThumbs && (options.showThumbs === 'none' || cursor.showThumbs === 'none') : false;
            cursor.triggerThumbVisibilityChanged = showChanged && finalShowThumbs !== 'none' || showThumbsMakesVisibilityChange && finalShow;
        }

        function dispatchThumbEvent(name, properties) {
            var event = new CustomEvent(name, { detail: properties });
            plot.getPlaceholder()[0].dispatchEvent(event);
        }

        function tryDispatchThumbVisibilityChangedEvent(cursor, visible) {
            cursor.triggerThumbVisibilityChanged && cursor.thumbs.forEach(function (thumb) {
                dispatchThumbEvent('thumbVisibilityChanged', { visible: visible, current: thumb });
            });
        }

        var reserveSpaceForThumbs = function reserveSpaceForThumbs(cursors, plot, offset) {
            var positions = ['left', 'right', 'top', 'bottom'],
                offsetIncrement = [],
                index = 0,
                thumbSize = 2 * constants.thumbRadius,
                thumbsPresent,
                axesPresent,
                axes,
                direction;

            offsetIncrement = positions.map(function (position, index) {
                thumbsPresent = cursors.some(function (cursor) {
                    return cursor.showThumbs.indexOf(position[0]) !== -1;
                });

                axes = index > 1 ? plot.getXAxes() : plot.getYAxes();
                axesPresent = axes.some(function (axis) {
                    return axis.options.position === position;
                });

                return thumbsPresent && !axesPresent ? thumbSize : 0;
            });

            for (direction in offset) {
                offset[direction] += offsetIncrement[index++];
            }
        };

        var selectedCursor = function selectedCursor(cursors) {
            var result;

            cursors.forEach(function (cursor) {
                if (cursor.selected) {
                    if (!result) {
                        result = cursor;
                    }
                }
            });

            return result;
        };

        var visibleCursors = function visibleCursors(cursors) {
            return cursors.filter(function (cursor) {
                return cursor.show;
            });
        };

        var pan = {
            start: function start(e) {
                var _getPlotXY = getPlotXY(e.detail.touches[0].pageX, e.detail.touches[0].pageY),
                    _getPlotXY2 = _slicedToArray(_getPlotXY, 2),
                    x = _getPlotXY2[0],
                    y = _getPlotXY2[1];

                handleCursorMoveStart(e, x, y, e.detail.touches[0].pageX, e.detail.touches[0].pageY);
            },

            drag: function drag(e) {
                var _getPlotXY3 = getPlotXY(e.detail.touches[0].pageX, e.detail.touches[0].pageY),
                    _getPlotXY4 = _slicedToArray(_getPlotXY3, 2),
                    x = _getPlotXY4[0],
                    y = _getPlotXY4[1];

                handleCursorMove(e, x, y, e.detail.touches[0].pageX, e.detail.touches[0].pageY);
            },

            end: function end(e) {
                handleCursorMoveEnd(e);
            }
        };

        var pinch = {
            start: function start(e) {
                var _getPlotXY5 = getPlotXY(e.detail.touches[0].pageX, e.detail.touches[0].pageY),
                    _getPlotXY6 = _slicedToArray(_getPlotXY5, 2),
                    x = _getPlotXY6[0],
                    y = _getPlotXY6[1];

                handleCursorMoveStart(e, x, y, e.detail.touches[0].pageX, e.detail.touches[0].pageY);
            },

            drag: function drag(e) {
                var _getPlotXY7 = getPlotXY(e.detail.touches[0].pageX, e.detail.touches[0].pageY),
                    _getPlotXY8 = _slicedToArray(_getPlotXY7, 2),
                    x = _getPlotXY8[0],
                    y = _getPlotXY8[1];

                handleCursorMove(e, x, y, e.detail.touches[0].pageX, e.detail.touches[0].pageY);
            },

            end: function end(e) {
                handleCursorMoveEnd(e);
            }
        };

        var thumbmove = {
            start: function start(e) {
                handleThumbMoveStart(e);
            },

            drag: function drag(e) {
                var _getPlotXY9 = getPlotXY(e.pageX, e.pageY),
                    _getPlotXY10 = _slicedToArray(_getPlotXY9, 2),
                    x = _getPlotXY10[0],
                    y = _getPlotXY10[1];

                handleThumbMove(e, x, y);
            },

            end: function end(e) {
                handleCursorMoveEnd(e);
            }
        };

        var mouseMove = {
            start: function start(e) {
                var page = $.plot.browser.getPageXY(e);

                var _getPlotXY11 = getPlotXY(page.X, page.Y),
                    _getPlotXY12 = _slicedToArray(_getPlotXY11, 2),
                    x = _getPlotXY12[0],
                    y = _getPlotXY12[1];

                handleCursorMoveStart(e, x, y, page.X, page.Y);
            },

            drag: function drag(e) {
                var page = $.plot.browser.getPageXY(e);

                var _getPlotXY13 = getPlotXY(page.X, page.Y),
                    _getPlotXY14 = _slicedToArray(_getPlotXY13, 2),
                    x = _getPlotXY14[0],
                    y = _getPlotXY14[1];

                handleCursorMove(e, x, y, page.X, page.Y);
            },

            mouseover: function mouseover(e) {
                var page = $.plot.browser.getPageXY(e);

                var _getPlotXY15 = getPlotXY(page.X, page.Y),
                    _getPlotXY16 = _slicedToArray(_getPlotXY15, 2),
                    x = _getPlotXY16[0],
                    y = _getPlotXY16[1];

                handleCursorMouseOver(e, x, y, page.X, page.Y);
            },

            end: function end(e) {
                var page = $.plot.browser.getPageXY(e);

                var _getPlotXY17 = getPlotXY(page.X, page.Y),
                    _getPlotXY18 = _slicedToArray(_getPlotXY17, 2),
                    x = _getPlotXY18[0],
                    y = _getPlotXY18[1];

                handleCursorMoveEnd(e, x, y, page.X, page.Y);
            }
        };

        plot.hooks.bindEvents.push(function (plot, eventHolder) {
            eventHolder[0].addEventListener('panstart', pan.start, false);
            eventHolder[0].addEventListener('pandrag', pan.drag, false);
            eventHolder[0].addEventListener('panend', pan.end, false);
            eventHolder[0].addEventListener('pinchstart', pinch.start, false);
            eventHolder[0].addEventListener('pinchdrag', pinch.drag, false);
            eventHolder[0].addEventListener('pinchend', pinch.end, false);

            plot.getPlaceholder().bind('plotpan', switchToAxisPlotPosition);
            plot.getPlaceholder().bind('plotzoom', switchToAxisPlotPosition);

            eventHolder[0].addEventListener('thumbmovestart', thumbmove.start, false);
            eventHolder[0].addEventListener('thumbmove', thumbmove.drag, false);
            eventHolder[0].addEventListener('thumbmoveend', thumbmove.end, false);

            plot.addEventHandler("dragstart", mouseMove.start, eventHolder, 10);
            plot.addEventHandler("drag", mouseMove.drag, eventHolder, 10);
            plot.addEventHandler("dragend", mouseMove.end, eventHolder, 10);
            eventHolder.bind('mousemove', mouseMove.mouseover);
        });

        plot.hooks.processRawData.push(function (plot, series, seriesData, datapoints) {
            if (series.historyBuffer) {
                // charting plugin enabled
                switchToRelativePlotPosition();
                snapMode = snapModes.x;
            }
        });

        function switchToRelativePlotPosition() {
            positionMode = positionModes.relative;
            cursors.forEach(function (cursor) {
                if (isNaN(cursor.x) || isNaN(cursor.y)) {
                    return;
                }
                var position = {
                    relativeX: cursor.position.relativeX || cursor.x / plot.width(),
                    relativeY: cursor.position.relativeY || cursor.y / plot.height(),
                    x: undefined,
                    y: undefined
                };
                if (!isNaN(position.relativeX) && !isNaN(position.relativeY)) {
                    cursor.position = position;
                }
            });
        }

        function switchToAxisPlotPosition() {
            positionMode = positionModes.axis;
            cursors.forEach(function (cursor) {
                cursor.mousePosition = {}; // reset mouse position
                if (cursor.position.x === undefined) {
                    var xaxis = findXAxis(plot, cursor);
                    cursor.position.x = xaxis.c2p ? xaxis.c2p(cursor.x) : undefined;
                }
                if (cursor.position.y === undefined) {
                    var yaxis = findYAxis(plot, cursor);
                    cursor.position.y = yaxis.c2p ? yaxis.c2p(cursor.y) : undefined;
                }
                cursor.position.relativeX = undefined;
                cursor.position.relativeY = undefined;
            });
        }

        function findIntersections(plot, cursor) {
            var intersections = {
                cursor: cursor.name,
                points: []
            };
            if (cursor.interpolate) {
                findIntersectionByInterpolation(plot, cursor, intersections);
            } else {
                findNearbyPointsIntersection(plot, cursor, intersections);
            }

            return intersections;
        }

        function findNearbyPointsIntersection(plot, cursor, intersections) {
            var cursorLastX, cursorLastY;
            if (positionMode === positionModes.axis) {
                cursorLastX = cursor.x;
                cursorLastY = cursor.y;
            } else {
                cursorLastX = cursor.mousePosition.relativeX * plot.width();
                cursorLastY = cursor.mousePosition.relativeY * plot.height();
            }
            var nearestPoint = plot.findNearbyItem(cursorLastX, cursorLastY, function (seriesIndex) {
                return cursor.snapToPlot === -1 || seriesIndex === cursor.snapToPlot;
            }, Number.MAX_VALUE, function (x, y) {
                // on a chart update, we want to snap to the nearest point to the cursor on the x axis
                // all other times, consider y value as well
                if (snapMode === snapModes.x) {
                    return x;
                }
                return x * x + y * y * 0.025;
            });

            if (nearestPoint) {
                var dataset = plot.getData(),
                    ps = dataset[nearestPoint.seriesIndex].datapoints.pointsize,
                    i = nearestPoint.dataIndex * ps;

                intersections.points.push({
                    x: nearestPoint.datapoint[0],
                    y: nearestPoint.datapoint[1],
                    leftPoint: [i - ps, i - ps + 1],
                    rightPoint: [i, i + 1],
                    seriesIndex: nearestPoint.seriesIndex
                });
            }
        }

        function findIntersectionByInterpolation(plot, cursor, intersections) {
            var pos = plot.c2p({
                left: cursor.x,
                top: cursor.y
            });

            var axes = plot.getAxes();
            if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max || pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
                return;
            }

            var interpolationPoint = plot.findNearbyInterpolationPoint(pos.x, pos.y, function (seriesIndex) {
                return cursor.snapToPlot === -1 || seriesIndex === cursor.snapToPlot;
            });

            if (interpolationPoint) {
                intersections.points.push({
                    x: interpolationPoint.datapoint[0],
                    y: interpolationPoint.datapoint[1],
                    leftPoint: interpolationPoint.leftPoint,
                    rightPoint: interpolationPoint.rightPoint,
                    seriesIndex: interpolationPoint.seriesIndex
                });
            }
        }

        function handleThumbMoveStart(e) {
            function isMovable(thumb) {
                return thumb.getAttribute('class').includes('draggable');
            }

            function extractTarget(touchedEl) {
                var target = touchedEl,
                    elClass = touchedEl.getAttribute('class');
                if (elClass.includes('thumbIcon') || elClass.includes('thumbLabel') || elClass.includes('interactionLayer')) {
                    target = touchedEl.parentNode;
                    elClass = target.getAttribute('class');
                }
                if (elClass.includes('thumb')) {
                    return target;
                }

                return null;
            }

            var currentlySelectedCursor = selectedCursor(cursors);
            if (!currentlySelectedCursor) {
                // find nearby cursor and unlock it
                visibleCursors(cursors).filter(function (cursor) {
                    return cursor.movable === true;
                }).forEach(function (cursor) {
                    cursor.thumbs.filter(isMovable).filter(function (t) {
                        return t === extractTarget(e.detail.target);
                    }).forEach(function (thumb) {
                        var targetCursor = cursor;
                        if (targetCursor) {
                            if (thumb.classList.contains('x')) {
                                targetCursor.dragmode = 'x';
                            } else {
                                targetCursor.dragmode = 'y';
                            }
                            targetCursor.selected = true;
                            targetCursor.thumbmove = true;

                            if (targetCursor.mode === 'x') {
                                plot.getPlaceholder().css('cursor', 'ew-resize');
                            } else if (targetCursor.mode === 'y') {
                                plot.getPlaceholder().css('cursor', 'ns-resize');
                            }

                            plot.getPlaceholder().css('cursor', 'move');
                            plot.getPlaceholder().trigger('cursordragstart', targetCursor);
                            plot.triggerRedrawOverlay();
                            e.stopImmediatePropagation();
                            e.preventDefault();
                        }
                    });
                });
            }
        }

        function handleThumbMove(e, x, y) {
            var currentlySelectedCursor = selectedCursor(cursors);

            if (currentlySelectedCursor) {
                updateCursorPositionOnMove(currentlySelectedCursor, x, y);
                var thumb = e.selectedThumb;
                var position = void 0;
                if (thumb.classList.contains('x')) {
                    position = currentlySelectedCursor.x + plot.getPlotOffset().left;
                } else {
                    position = currentlySelectedCursor.y + plot.getPlotOffset().top;
                }
                thumbPreviousPositionMap.set(thumb, position);

                plot.getPlaceholder().trigger('cursordrag', currentlySelectedCursor);
                plot.triggerRedrawOverlay();
                e.stopImmediatePropagation();
                e.preventDefault();
            }
        }

        function handleCursorMove(e, x, y, pageX, pageY) {
            var currentlySelectedCursor = selectedCursor(cursors);

            if (currentlySelectedCursor) {
                updateCursorPositionOnMove(currentlySelectedCursor, x, y);

                plot.getPlaceholder().trigger('cursordrag', currentlySelectedCursor);
                plot.triggerRedrawOverlay();
                e.stopImmediatePropagation();
                e.preventDefault();
            }
        }

        function handleCursorMouseOver(e, x, y, pageX, pageY) {
            var currentlySelectedCursor = selectedCursor(cursors);

            if (currentlySelectedCursor && e.buttons === 0) {
                currentlySelectedCursor.selected = false;
                plot.triggerRedrawOverlay();
            }

            visibleCursors(cursors).forEach(function (cursor) {
                updateCursorHightlight(pageX, pageY, plot, cursor);
            });
        }

        function updateCursorHightlight(pageX, pageY, plot, cursor) {
            if (!cursor.movable) {
                return;
            }
            if (mouseOverCursorManipulator(pageX, pageY, plot, cursor)) {
                if (!cursor.highlighted) {
                    cursor.highlighted = true;
                    plot.triggerRedrawOverlay();
                }

                plot.getPlaceholder().css('cursor', 'pointer');
            } else if (mouseOverCursorVerticalLine(pageX, pageY, plot, cursor)) {
                if (!cursor.highlighted) {
                    cursor.highlighted = true;
                    plot.triggerRedrawOverlay();
                }

                plot.getPlaceholder().css('cursor', 'col-resize');
            } else if (mouseOverCursorHorizontalLine(pageX, pageY, plot, cursor)) {
                if (!cursor.highlighted) {
                    cursor.highlighted = true;
                    plot.triggerRedrawOverlay();
                }

                plot.getPlaceholder().css('cursor', 'row-resize');
            } else {
                if (cursor.highlighted) {
                    cursor.highlighted = false;
                    plot.getPlaceholder().css('cursor', 'default');
                    plot.triggerRedrawOverlay();
                }
            }
        }

        function handleCursorMoveStart(e, x, y, pageX, pageY) {
            var currentlySelectedCursor = selectedCursor(cursors);
            snapMode = snapModes.xy;
            if (currentlySelectedCursor) {
                plot.getPlaceholder().css('cursor', 'default');
                currentlySelectedCursor.x = x;
                currentlySelectedCursor.y = y;
                currentlySelectedCursor.position.relativeX = x / plot.width();
                currentlySelectedCursor.position.relativeY = y / plot.height();

                plot.triggerRedrawOverlay();
            } else {
                // find nearby cursor and unlock it
                var targetCursor;
                var dragmode;

                visibleCursors(cursors).forEach(function (cursor) {
                    if (!cursor.movable) {
                        return;
                    }
                    if (mouseOverCursorManipulator(pageX, pageY, plot, cursor)) {
                        targetCursor = cursor;
                        dragmode = 'xy';
                    } else if (mouseOverCursorHorizontalLine(pageX, pageY, plot, cursor)) {
                        targetCursor = cursor;
                        dragmode = 'y';
                    } else if (mouseOverCursorVerticalLine(pageX, pageY, plot, cursor)) {
                        targetCursor = cursor;
                        dragmode = 'x';
                    }

                    updateCursorHightlight(pageX, pageY, plot, cursor);
                });

                if (targetCursor) {
                    targetCursor.thumbmove = false;
                    targetCursor.selected = true;
                    targetCursor.dragmode = dragmode;
                    // changed for InsightCM -max
                    if (targetCursor.mode === 'x') {
                        plot.getPlaceholder().css('cursor', 'ew-resize');
                    } else if (targetCursor.mode === 'y') {
                        plot.getPlaceholder().css('cursor', 'ns-resize');
                    } else {
                        plot.getPlaceholder().css('cursor', 'move');
                    }
                    plot.getPlaceholder().css('cursor', 'move');
                    plot.getPlaceholder().trigger('cursordragstart', targetCursor);
                    plot.triggerRedrawOverlay();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                }
            }

            currentlySelectedCursor = selectedCursor(cursors);
        }

        function handleCursorMoveEnd(e, x, y, pageX, pageY) {
            var currentlySelectedCursor = selectedCursor(cursors);

            if (currentlySelectedCursor) {
                if (isFinite(x) && isFinite(y)) {
                    updateCursorPositionOnMove(currentlySelectedCursor, x, y);
                }

                // lock the free cursor to current position
                currentlySelectedCursor.selected = false;
                currentlySelectedCursor.thumbmove = false;

                plot.getPlaceholder().css('cursor', 'default');
                plot.getPlaceholder().trigger('cursordragend', currentlySelectedCursor);
                plot.triggerRedrawOverlay();
                e.stopImmediatePropagation();
                e.preventDefault();
            }
        }

        function updateCursorPositionOnMove(cursor, x, y) {
            var xaxis = findXAxis(plot, cursor),
                yaxis = findYAxis(plot, cursor);

            if (cursor.dragmode.indexOf('x') !== -1) {
                cursor.x = x;
                cursor.position.x = xaxis.c2p ? xaxis.c2p(x) : undefined;
                cursor.position.relativeX = x / plot.width();
                cursor.mousePosition.relativeX = x / plot.width();
            }

            if (cursor.dragmode.indexOf('y') !== -1) {
                cursor.y = y;
                cursor.position.y = yaxis.c2p ? yaxis.c2p(y) : undefined;
                cursor.position.relativeY = y / plot.height();
                cursor.mousePosition.relativeY = y / plot.height();
            }
        }

        function getPlotXY(pageX, pageY) {
            if (plotPositionConstrain) {
                return plotPositionConstrain(pageX, pageY);
            }

            var offset = plot.offset();
            return [Math.max(0, Math.min(pageX - offset.left, plot.width())), Math.max(0, Math.min(pageY - offset.top, plot.height()))];
        }

        plot.hooks.drawOverlay.push(function (plot, ctx) {
            var isMousePositionInitilized = function isMousePositionInitilized(mousePosition) {
                return mousePosition.relativeX !== undefined && mousePosition.relativeY !== undefined;
            };

            update = [];

            cursors.forEach(function (cursor) {
                var intersections;

                initializePositionIfNecessary(plot, cursor);
                setPosition(plot, cursor, cursor.position);
                if (!isMousePositionInitilized(cursor.mousePosition)) {
                    cursor.mousePosition.relativeX = cursor.x / plot.width();
                    cursor.mousePosition.relativeY = cursor.y / plot.height();
                }

                intersections = findIntersections(plot, cursor);
                maybeSnapToPlot(plot, cursor, intersections);
                cursor.intersections = intersections;
                intersections.target = cursor;
                update.push(intersections);

                if (cursor.show) {
                    var plotOffset = plot.getPlotOffset();

                    ctx.save();
                    ctx.translate(plotOffset.left, plotOffset.top);

                    determineAndSetTextQuadrant(plot, ctx, cursor);
                    drawVerticalAndHorizontalLines(plot, ctx, cursor);
                    drawLabel(plot, ctx, cursor);
                    drawIntersections(plot, ctx, cursor);
                    drawValues(plot, ctx, cursor);
                    if (cursor.symbol !== 'none') {
                        drawManipulator(plot, ctx, cursor);
                    }

                    ctx.restore();

                    if (cursor.showThumbs !== 'none') {
                        if (cursor.thumbs.length === 0 || cursor._createThumbs) {
                            createThumbs(cursor, plot, plotOffset);
                            cursor._createThumbs = false;
                        } else if (!cursor.thumbmove) {
                            //the case where cursor was moved not due to a thumb move
                            updateThumbsPosition(plot, cursor, plotOffset);
                            tryDispatchThumbVisibilityChangedEvent(cursor, true);
                        }
                    } else {
                        hideThumbs(cursor);
                        tryDispatchThumbVisibilityChangedEvent(cursor, false);
                    }
                } else {
                    hideThumbs(cursor);
                    tryDispatchThumbVisibilityChangedEvent(cursor, false);
                }
            });

            if (update.length > 0) {
                plot.getPlaceholder().trigger('cursorupdates', [update]);
            }
        });

        plot.hooks.resize.push(function (plot, width, height) {
            // the height and the width parameter from resize hook include margin offsets
            cursors.forEach(function (cursor) {
                var plotOffset = plot.getPlotOffset();
                var yThumbIndex = 0;

                var xThumb = cursor.thumbs[0];
                if (xAxisThumb(cursor) && xThumb) {
                    yThumbIndex = 1;
                    if (cursor.showThumbs.indexOf('b') !== -1) {
                        var thumbCy = height - plotOffset.bottom + constants.thumbRadius;
                        $.thumb.updateComputedYPosition(xThumb, thumbCy);
                    }
                }

                var yThumb = cursor.thumbs[yThumbIndex];
                if (yAxisThumb(cursor) && yThumb) {
                    if (cursor.showThumbs.indexOf('r') !== -1) {
                        var thumbCx = width - plotOffset.right + constants.thumbRadius;
                        $.thumb.updateComputedXPosition(yThumb, thumbCx);
                    }
                }
            });
        });

        function hideThumbs(cursor) {
            cursor.thumbs.forEach(function (thumb) {
                $.thumb.setHidden(thumb, true);
            });
        }

        function createThumbs(cursor, plot, plotOffset) {
            svgRoot = $.thumb.createSVGLayer(plot.getPlaceholder(), plot.getEventHolder());
            var thumb = void 0,
                thumbCx = void 0,
                thumbCy = void 0,
                constraintFunction = void 0,
                yThumbIndex = 0;
            var thumbOptions = {
                size: constants.thumbRadius,
                svgRoot: svgRoot,
                abbreviation: cursor.thumbAbbreviation || cursor.name[0],
                classList: cursor.thumbClassList,
                offset: cursor.thumbOffset,
                fill: cursor.thumbColor
            };

            if (xAxisThumb(cursor) && !cursor.thumbs[0]) {
                yThumbIndex = 1;
                thumbCx = cursor.x + plotOffset.left;
                thumbCy = cursor.showThumbs.indexOf('b') !== -1 ? plot.height() + plotOffset.top + constants.thumbRadius : plotOffset.top - constants.thumbRadius;
                // cursor uses the default constrain function unless we provide one explicitly
                constraintFunction = cursor.horizontalThumbConstrain ? cursor.horizontalThumbConstrain : function (mouseX, mouseY, currentX, currentY) {
                    var offsetLeft = plot.offset().left,
                        x = Math.max(offsetLeft, Math.min(mouseX, plot.width() + offsetLeft));
                    return [x, currentY];
                };

                thumbOptions.shape = cursor.showThumbs.indexOf('b') !== -1 ? 'bottom' : 'top';
                thumbOptions.x = thumbCx;
                thumbOptions.y = thumbCy;
                thumbOptions.constraintFunction = constraintFunction;

                thumb = $.thumb.createThumb(thumbOptions);
                cursor.thumbs[0] = thumb;
                thumbPreviousPositionMap.set(thumb, thumbCx);
                dispatchThumbEvent('thumbCreated', { current: thumb });
            }
            if (yAxisThumb(cursor) && !cursor.thumbs[yThumbIndex]) {
                thumbCx = cursor.showThumbs.indexOf('l') !== -1 ? plotOffset.left - constants.thumbRadius : plotOffset.left + plot.width() + constants.thumbRadius;
                thumbCy = cursor.y + plotOffset.top;
                constraintFunction = cursor.verticalThumbConstrain ? cursor.verticalThumbConstrain : function (mouseX, mouseY, currentX, currentY) {
                    var offset = plot.offset(),
                        y = Math.max(offset.top, Math.min(mouseY, plot.height() + offset.top));
                    return [currentX, y];
                };

                thumbOptions.shape = cursor.showThumbs.indexOf('l') !== -1 ? 'left' : 'right';
                thumbOptions.x = thumbCx;
                thumbOptions.y = thumbCy;
                thumbOptions.constraintFunction = constraintFunction;

                thumb = $.thumb.createThumb(thumbOptions);
                cursor.thumbs[yThumbIndex] = thumb;
                thumbPreviousPositionMap.set(thumb, thumbCy);
                dispatchThumbEvent('thumbCreated', { current: thumb });
            }
        }

        function updateThumbsPosition(plot, cursor, plotOffset) {
            var thumb = void 0;
            if (xAxisThumb(cursor)) {
                thumb = cursor.thumbs[0];
                var cursorX = cursor.x + plotOffset.left;
                var previousX = thumbPreviousPositionMap.get(thumb);
                var isTopThumb = isThumbAtTop(thumb);
                if (isPositionInXRange(plot, cursor.x)) {
                    if (previousX < plotOffset.left) {
                        dispatchThumbIntoOutOfRangeEvent('thumbIntoRange', {
                            target: thumb,
                            edge: isTopThumb ? 'topleft' : 'bottomleft',
                            orientation: 'horizontal',
                            position: cursorX
                        }, plot.getEventHolder());
                    } else if (previousX > plotOffset.left + plot.width()) {
                        dispatchThumbIntoOutOfRangeEvent('thumbIntoRange', {
                            target: thumb,
                            edge: isTopThumb ? 'topright' : 'bottomright',
                            orientation: 'horizontal',
                            position: cursorX
                        }, plot.getEventHolder());
                    }

                    $.thumb.setHidden(thumb, false);
                    $.thumb.updateComputedXPosition(thumb, cursorX);
                } else {
                    if (isPositionInXRange(plot, previousX - plotOffset.left) && cursor.x < 0) {
                        dispatchThumbIntoOutOfRangeEvent('thumbOutOfRange', {
                            target: thumb,
                            edge: isTopThumb ? 'topleft' : 'bottomleft',
                            orientation: 'horizontal'
                        }, plot.getEventHolder());
                    } else if (isPositionInXRange(plot, previousX - plotOffset.left) && cursor.x > plot.width()) {
                        dispatchThumbIntoOutOfRangeEvent('thumbOutOfRange', {
                            target: thumb,
                            edge: isTopThumb ? 'topright' : 'bottomright',
                            orientation: 'horizontal'
                        }, plot.getEventHolder());
                    }

                    $.thumb.setHidden(thumb, true);
                }

                thumbPreviousPositionMap.set(thumb, cursorX);
            }
            if (yAxisThumb(cursor)) {
                var yThumbIndex = xAxisThumb(cursor) ? 1 : 0;
                thumb = cursor.thumbs[yThumbIndex];
                var cursorY = cursor.y + plotOffset.top;
                var previousY = thumbPreviousPositionMap.get(thumb);
                var isLeftThumb = isThumbAtLeft(thumb);
                if (isPositionInYRange(plot, cursor.y)) {
                    if (previousY < plotOffset.top) {
                        dispatchThumbIntoOutOfRangeEvent('thumbIntoRange', {
                            target: thumb,
                            edge: isLeftThumb ? 'topleft' : 'topright',
                            orientation: 'vertical',
                            position: cursorY
                        }, plot.getEventHolder());
                    } else if (previousY > plotOffset.top + plot.height()) {
                        dispatchThumbIntoOutOfRangeEvent('thumbIntoRange', {
                            target: thumb,
                            edge: isLeftThumb ? 'bottomleft' : 'bottomright',
                            orientation: 'vertical',
                            position: cursorY
                        }, plot.getEventHolder());
                    }

                    $.thumb.setHidden(thumb, false);
                    $.thumb.updateComputedYPosition(thumb, cursorY);
                } else {
                    if (isPositionInYRange(plot, previousY - plotOffset.top)) {
                        if (cursor.y < 0) {
                            dispatchThumbIntoOutOfRangeEvent('thumbOutOfRange', {
                                target: thumb,
                                edge: isLeftThumb ? 'topleft' : 'topright',
                                orientation: 'vertical'
                            }, plot.getEventHolder());
                        } else if (cursor.y > plot.height()) {
                            dispatchThumbIntoOutOfRangeEvent('thumbOutOfRange', {
                                target: thumb,
                                edge: isLeftThumb ? 'bottomleft' : 'bottomright',
                                orientation: 'vertical'
                            }, plot.getEventHolder());
                        }
                    }

                    $.thumb.setHidden(thumb, true);
                }

                thumbPreviousPositionMap.set(thumb, cursorY);
            }
        }

        function dispatchThumbIntoOutOfRangeEvent(eventType, properties, eventHolder) {
            var event = new CustomEvent(eventType, { detail: properties });
            eventHolder.dispatchEvent(event);
        }

        function isThumbAtTop(thumb) {
            return thumb.classList.contains('top');
        }

        function isThumbAtLeft(thumb) {
            return thumb.classList.contains('left');
        }

        function xAxisThumb(cursor) {
            return cursor.showThumbs.indexOf('b') !== -1 || cursor.showThumbs.indexOf('t') !== -1;
        }

        function yAxisThumb(cursor) {
            return cursor.showThumbs.indexOf('l') !== -1 || cursor.showThumbs.indexOf('r') !== -1;
        }

        plot.hooks.shutdown.push(function (plot, eventHolder) {
            eventHolder[0].removeEventListener('panstart', pan.start);
            eventHolder[0].removeEventListener('pandrag', pan.drag);
            eventHolder[0].removeEventListener('panend', pan.end);
            eventHolder[0].removeEventListener('pinchstart', pinch.start);
            eventHolder[0].removeEventListener('pinchdrag', pinch.drag);
            eventHolder[0].removeEventListener('pinchend', pinch.end);

            eventHolder[0].removeEventListener('thumbmovestart', thumbmove.start);
            eventHolder[0].removeEventListener('thumbmove', thumbmove.drag);
            eventHolder[0].removeEventListener('thumbmoveend', thumbmove.end);

            eventHolder.unbind('dragstart', mouseMove.start);
            eventHolder.unbind('drag', mouseMove.drag);
            eventHolder.unbind('dragend', mouseMove.end);
            eventHolder.unbind('mousemove', mouseMove.mouseover);

            eventHolder.unbind('cursorupdates');

            var placeholder = plot.getPlaceholder();
            placeholder.unbind('plotpan', switchToAxisPlotPosition);
            placeholder.unbind('plotzoom', switchToAxisPlotPosition);
            placeholder.css('cursor', 'default');
            if (placeholder.find('.flot-thumbs')[0]) {
                $.thumb.shutdown(placeholder.find('.flot-thumbs')[0].firstChild);
            }

            cursors = [];
            thumbPreviousPositionMap.clear();
            update = [];
        });
    }

    function mixin(source, destination) {
        Object.keys(source).forEach(function (key) {
            destination[key] = source[key];
        });

        return destination;
    }

    function initializePositionIfNecessary(plot, cursor) {
        var xaxis = findXAxis(plot, cursor),
            yaxis = findYAxis(plot, cursor),
            position = cursor.position;
        if ((position.x === undefined || position.y === undefined) && xaxis.c2p && yaxis.c2p && !(xaxis.min === 0 && xaxis.max === 1 && yaxis.min === 0 && yaxis.max === 1)) {
            // if cursor is defined with an initial relative position, need to initialize
            // axis position values after axes have been defined
            position.x = xaxis.c2p(position.relativeX * plot.width());
            position.y = yaxis.c2p(position.relativeY * plot.height());
        }

        if ((position.relativeX === undefined || position.relativeY === undefined) && xaxis.p2c && yaxis.p2c && !(position.x === undefined || position.y === undefined)) {
            // if cursor is defined with an initial axis position, need to initialize
            // axis relative position values
            position.relativeX = xaxis.p2c(position.x) / plot.width();
            position.relativeY = yaxis.p2c(position.y) / plot.height();
        }
    }

    /**
        Calculate and set the canvas coords based on relative coords or plot values.
        When both provided then the plot values will be taken into account
        and the canvas coords ignored.
     */
    function setPosition(plot, cursor, pos) {
        var xaxis = findXAxis(plot, cursor),
            yaxis = findYAxis(plot, cursor),
            x,
            y;

        if (pos.x !== undefined && xaxis.p2c) {
            x = xaxis.p2c(pos.x);
        } else {
            x = pos.relativeX * plot.width();
        }
        if (pos.y !== undefined && yaxis.p2c) {
            y = yaxis.p2c(pos.y);
        } else {
            y = pos.relativeY * plot.height();
        }

        // These values could be outside the plot area
        cursor.x = x;
        cursor.y = y;
    }

    function maybeSnapToPlot(plot, cursor, intersections) {
        if (cursor.snapToPlot >= -1) {
            var point = intersections.points[0];

            if (point) {
                var plotData = plot.getData()[point.seriesIndex],
                    relativeX = plotData.xaxis.p2c(point.x) / plot.width(),
                    relativeY = plotData.yaxis.p2c(point.y) / plot.height();

                setPosition(plot, cursor, {
                    x: point.x,
                    y: point.y
                });

                cursor.position.x = point.x;
                cursor.position.y = point.y;
                cursor.position.relativeX = relativeX;
                cursor.position.relativeY = relativeY;

                intersections.x = point.x; // update cursor position
                intersections.y = point.y;
            }
        }
    }

    function determineAndSetTextQuadrant(plot, ctx, cursor) {
        var width = plot.width(),
            height = plot.height(),
            y = cursor.y,
            x = cursor.x,
            rowsWidth = 0,
            rowsHeight = 0,
            count = rowCount(cursor),
            fontSizeInPx = Number(cursor.fontSize.substring(0, cursor.fontSize.length - 2)),
            lowerLimit = 0.05,
            higherLimit = 1 - lowerLimit;

        ctx.font = cursor.fontStyle + ' ' + cursor.fontWeight + ' ' + cursor.fontSize + ' ' + cursor.fontFamily;
        if (cursor.showLabel) {
            rowsWidth = Math.max(rowsWidth, ctx.measureText(cursor.name).width);
        }
        if (cursor.showValues) {
            var positionTextValues = formatCursorPosition(plot, cursor),
                text = positionTextValues.xTextValue + ", " + positionTextValues.yTextValue;
            rowsWidth = Math.max(rowsWidth, ctx.measureText(text).width);
        }

        if (cursor.halign === 'right' && x + rowsWidth > width * higherLimit) {
            cursor.halign = 'left';
        } else if (cursor.halign === 'left' && x - rowsWidth < width * lowerLimit) {
            cursor.halign = 'right';
        }

        rowsHeight = count * (fontSizeInPx + constants.labelPadding);

        if (cursor.valign === 'below' && y + rowsHeight > height * higherLimit) {
            cursor.valign = 'above';
        } else if (cursor.valign === 'above' && y - rowsHeight < height * lowerLimit) {
            cursor.valign = 'below';
        }
    }

    /**
     * The text displayed next to the cursor can be stacked as rows and their positions can be calculated with this function.
     * The bottom one has the index = 0, and the top one has the index = count -1. Depending on the current cursor's possition
     * relative to the center of the plot, index and count, the positions will be computed like this:
     *
     *               |
     *           two | two
     *           one | one
     *          zero | zero
     *       --------+--------
     *           two | two
     *           one | one
     *          zero | zero
     *               |
     */
    function computeRowPosition(plot, cursor, index, count) {
        var textAlign = 'left';
        var fontSizeInPx = Number(cursor.fontSize.substring(0, cursor.fontSize.length - 2));

        var y = cursor.y;
        var x = cursor.x;

        if (cursor.halign === 'left') {
            x -= constants.labelPadding;
            textAlign = 'right';
        } else {
            x += constants.labelPadding;
        }

        if (cursor.valign === 'above') {
            y -= constants.labelPadding * (count - index) + fontSizeInPx * (count - 1 - index);
        } else {
            y += constants.labelPadding * (index + 1) + fontSizeInPx * (index + 1);
        }

        return {
            x: x,
            y: y,
            textAlign: textAlign
        };
    }

    function rowCount(cursor) {
        return (cursor.showLabel ? 1 : 0) + (cursor.showValues ? 1 : 0);
    }

    function labelRowIndex(cursor) {
        return 0;
    }

    function valuesRowIndex(cursor) {
        return cursor.showLabel ? 1 : 0;
    }

    function isPositionInXRange(plot, position) {
        return position >= 0 && Math.round(position) <= plot.width();
    }

    function isPositionInYRange(plot, position) {
        return position >= 0 && Math.round(position) <= plot.height();
    }

    function cursorInPlotRange(plot, cursor) {
        return isPositionInXRange(plot, cursor.x) && isPositionInYRange(plot, cursor.y);
    }

    function drawLabel(plot, ctx, cursor) {
        if (cursor.showLabel && cursorInPlotRange(plot, cursor)) {
            ctx.beginPath();
            var position = computeRowPosition(plot, cursor, labelRowIndex(cursor), rowCount(cursor));
            ctx.fillStyle = cursor.color;
            ctx.textAlign = position.textAlign;
            ctx.font = cursor.fontStyle + ' ' + cursor.fontWeight + ' ' + cursor.fontSize + ' ' + cursor.fontFamily;
            ctx.fillText(cursor.name, position.x, position.y);
            ctx.textAlign = 'left';
            ctx.stroke();
        }
    }

    function fillTextAligned(ctx, text, x, y, position, fontStyle, fontWeight, fontSize, fontFamily) {
        var fontSizeInPx = Number(fontSize.substring(0, fontSize.length - 2));
        var textWidth;
        switch (position) {
            case 'left':
                textWidth = ctx.measureText(text).width;
                x = x - textWidth - constants.iRectSize;
                break;
            case 'bottom-left':
                textWidth = ctx.measureText(text).width;
                x = x - textWidth - constants.iRectSize;
                y = y + fontSizeInPx;
                break;
            case 'top-left':
                textWidth = ctx.measureText(text).width;
                x = x - textWidth - constants.iRectSize;
                y = y - constants.iRectSize;
                break;
            case 'top-right':
                x = x + constants.iRectSize;
                y = y - constants.iRectSize;
                break;
            case 'right':
                x = x + constants.iRectSize;
                break;
            case 'bottom-right':
            default:
                x = x + constants.iRectSize;
                y = y + fontSizeInPx;
                break;
        }

        ctx.textBaseline = "middle";
        ctx.font = fontStyle + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily;
        ctx.fillText(text, x, y);
    }

    function drawIntersections(plot, ctx, cursor) {
        if (cursor.showIntersections && hasVerticalLine(cursor)) {
            ctx.beginPath();
            if (cursor.intersections === undefined) {
                return;
            }
            cursor.intersections.points.forEach(function (point, index) {
                if (_typeof(cursor.showIntersections) === 'object') {
                    if (cursor.showIntersections.indexOf(index) === -1) {
                        return;
                    }
                }
                var coord = plot.p2c(point);
                ctx.fillStyle = cursor.intersectionColor;
                ctx.fillRect(Math.floor(coord.left) - constants.iRectSize / 2, Math.floor(coord.top) - constants.iRectSize / 2, constants.iRectSize, constants.iRectSize);

                var text;
                if (typeof cursor.formatIntersectionData === 'function') {
                    text = cursor.formatIntersectionData(point);
                } else {
                    text = point.y.toFixed(2);
                }

                fillTextAligned(ctx, text, coord.left, coord.top, cursor.intersectionLabelPosition, cursor.fontStyle, cursor.fontWeight, cursor.fontSize, cursor.fontFamily);
            });
            ctx.stroke();
        }
    }

    function computeCursorsPrecision(plot, axis, canvasPosition) {
        var canvas2 = axis.direction === "x" ? canvasPosition + 1 : canvasPosition - 1,
            point1 = axis.c2p(canvasPosition),
            point2 = axis.c2p(canvas2);

        return plot.computeValuePrecision(point1, point2, axis.direction, 1);
    }

    function findXAxis(plot, cursor) {
        var dataset = plot.getData(),
            xaxes = plot.getXAxes(),
            zeroBasedIndex = cursor.defaultxaxis - 1;
        if (cursor.snapToPlot >= -1) {
            if (cursor.intersections && cursor.intersections.points[0]) {
                var series = dataset[cursor.intersections.points[0].seriesIndex];
                return series ? series.xaxis : xaxes[zeroBasedIndex];
            } else {
                return xaxes[zeroBasedIndex];
            }
        } else {
            return xaxes[zeroBasedIndex];
        }
    }

    function findYAxis(plot, cursor) {
        var dataset = plot.getData(),
            yaxes = plot.getYAxes(),
            zeroBasedIndex = cursor.defaultyaxis - 1;
        if (cursor.snapToPlot >= -1) {
            if (cursor.intersections && cursor.intersections.points[0]) {
                var series = dataset[cursor.intersections.points[0].seriesIndex];
                return series ? series.yaxis : yaxes[zeroBasedIndex];
            } else {
                return yaxes[zeroBasedIndex];
            }
        } else {
            return yaxes[zeroBasedIndex];
        }
    }

    function formatCursorPosition(plot, cursor) {
        var xaxis = findXAxis(plot, cursor),
            yaxis = findYAxis(plot, cursor),
            htmlSpace = '&nbsp;',
            htmlNewline = '<br>',
            xaxisPrecision = computeCursorsPrecision(plot, xaxis, cursor.x),
            xFormattedValue = xaxis.tickFormatter(xaxis.c2p(cursor.x), xaxis, xaxisPrecision, plot),
            yaxisPrecision = computeCursorsPrecision(plot, yaxis, cursor.y),
            yFormattedValue = yaxis.tickFormatter(yaxis.c2p(cursor.y), yaxis, yaxisPrecision, plot);

        xFormattedValue = xFormattedValue.replace(htmlNewline, " ");
        xFormattedValue = xFormattedValue.replace(htmlSpace, " ");

        yFormattedValue = yFormattedValue.replace(htmlNewline, " ");
        yFormattedValue = yFormattedValue.replace(htmlSpace, " ");

        return {
            xTextValue: xFormattedValue,
            yTextValue: yFormattedValue
        };
    }

    function drawValues(plot, ctx, cursor) {
        if (cursor.showValues && cursorInPlotRange(plot, cursor)) {
            var positionTextValues = formatCursorPosition(plot, cursor),
                text = positionTextValues.xTextValue + ", " + positionTextValues.yTextValue,
                position = computeRowPosition(plot, cursor, valuesRowIndex(cursor), rowCount(cursor));

            ctx.fillStyle = cursor.color;
            ctx.textAlign = position.textAlign;
            ctx.font = cursor.fontStyle + ' ' + cursor.fontWeight + ' ' + cursor.fontSize + ' ' + cursor.fontFamily;
            ctx.fillText(text, position.x, position.y);
        }
    }

    function drawVerticalAndHorizontalLines(plot, ctx, cursor) {
        // abort draw if linewidth is zero
        if (cursor.lineWidth === 0) {
            return;
        }
        // keep line sharp
        var adj = cursor.lineWidth % 2 ? 0.5 : 0;

        ctx.strokeStyle = cursor.color;
        ctx.lineWidth = cursor.lineWidth;
        ctx.lineJoin = "round";

        ctx.beginPath();
        ctx.setLineDash(cursor.dashes);

        if (cursor.mode.indexOf("x") !== -1 && isPositionInXRange(plot, cursor.x)) {
            var drawX = Math.floor(cursor.x) + adj;
            // draw from cursor center out, so dashed lines look consistent
            ctx.moveTo(drawX, Math.min(plot.height(), Math.max(0, cursor.y - cursor.symbolSize / 2)));
            ctx.lineTo(drawX, 0);
            ctx.moveTo(drawX, Math.min(plot.height(), Math.max(0, cursor.y + cursor.symbolSize / 2)));
            ctx.lineTo(drawX, plot.height());
        }

        if (cursor.mode.indexOf("y") !== -1 && isPositionInYRange(plot, cursor.y)) {
            var drawY = Math.floor(cursor.y) + adj;
            // draw from cursor center out, so dashed lines look consistent
            ctx.moveTo(Math.min(plot.width(), Math.max(0, cursor.x - cursor.symbolSize / 2)), drawY);
            ctx.lineTo(0, drawY);
            ctx.moveTo(Math.min(plot.width(), Math.max(0, cursor.x + cursor.symbolSize / 2)), drawY);
            ctx.lineTo(plot.width(), drawY);
        }

        ctx.stroke();
        ctx.setLineDash([]);
    }

    function drawManipulator(plot, ctx, cursor) {
        if (!cursorInPlotRange(plot, cursor)) {
            return;
        }

        // keep line sharp
        var adj = cursor.lineWidth % 2 ? 0.5 : 0;
        ctx.beginPath();

        if (cursor.highlighted) {
            ctx.strokeStyle = 'orange';
        } else {
            ctx.strokeStyle = cursor.color;
        }
        if (cursor.symbol && plot.drawSymbol && plot.drawSymbol[cursor.symbol]) {
            ctx.fillStyle = cursor.color;
            plot.drawSymbol[cursor.symbol](ctx, Math.floor(cursor.x) + adj, Math.floor(cursor.y) + adj, cursor.symbolSize / 2, 0);
        } else {
            ctx.fillRect(Math.floor(cursor.x) + adj - cursor.symbolSize / 2, Math.floor(cursor.y) + adj - cursor.symbolSize / 2, cursor.symbolSize, cursor.symbolSize);
        }

        ctx.stroke();
    }

    function hasVerticalLine(cursor) {
        return cursor.mode.indexOf('x') !== -1;
    }

    function hasHorizontalLine(cursor) {
        return cursor.mode.indexOf('y') !== -1;
    }

    function mouseOverCursorManipulator(x, y, plot, cursor) {
        var offset = plot.offset();
        var mouseX = Math.max(0, Math.min(x - offset.left, plot.width()));
        var mouseY = Math.max(0, Math.min(y - offset.top, plot.height()));
        var grabRadius = cursor.symbolSize + constants.mouseGrabMargin;

        return mouseX > cursor.x - grabRadius && mouseX < cursor.x + grabRadius && mouseY > cursor.y - grabRadius && mouseY < cursor.y + grabRadius && cursor.symbol !== 'none';
    }

    function mouseOverCursorVerticalLine(x, y, plot, cursor) {
        var offset = plot.offset();
        var mouseX = Math.max(0, Math.min(x - offset.left, plot.width()));
        var mouseY = Math.max(0, Math.min(y - offset.top, plot.height()));

        return hasVerticalLine(cursor) && mouseX > cursor.x - constants.mouseGrabMargin && mouseX < cursor.x + constants.mouseGrabMargin && mouseY > 0 && mouseY < plot.height();
    }

    function mouseOverCursorHorizontalLine(x, y, plot, cursor) {
        var offset = plot.offset();
        var mouseX = Math.max(0, Math.min(x - offset.left, plot.width()));
        var mouseY = Math.max(0, Math.min(y - offset.top, plot.height()));

        return hasHorizontalLine(cursor) && mouseY > cursor.y - constants.mouseGrabMargin && mouseY < cursor.y + constants.mouseGrabMargin && mouseX > 0 && mouseX < plot.width();
    }

    $.plot.plugins.push({
        init: init,
        options: options,
        name: 'cursors',
        version: '0.2'
    });
})(jQuery);
//# sourceMappingURL=jquery.flot.cursors.js.map