From c26521356a1da84c005a65da012434d7698fe1c9 Mon Sep 17 00:00:00 2001 From: andrewdove1 Date: Tue, 11 Jun 2019 08:09:13 -0500 Subject: [PATCH 1/8] Copied from another fork and updated per comments --- docs/API.md | 50 +++++++++++++++++++++- source/jquery.flot.hover.js | 14 +++++- source/jquery.flot.js | 62 ++++++++++++++++++++------- tests/jquery.flot.hover.Test.js | 75 +++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 19 deletions(-) diff --git a/docs/API.md b/docs/API.md index 27dfaf74..c3b51bf2 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1032,7 +1032,7 @@ You can use "plotclick" and "plothover" events like this: ```js $.plot($("#placeholder"), [ d ], { grid: { clickable: true } }); -$("#placeholder").bind("plotclick", function (event, pos, item) { +$("#placeholder").bind("plotclick", function (event, pos, items) { alert("You clicked at " + pos.x + ", " + pos.y); // axis coordinates for other axes, if present, are in pos.x2, pos.x3, ... // if you need global screen coordinates, they are pos.pageX, pos.pageY @@ -1052,6 +1052,7 @@ item: { dataIndex: the index of the point in the data array series: the series object seriesIndex: the index of the series + distance: the distance from the cursor pageX, pageY: the global screen coordinates of the point } ``` @@ -1669,6 +1670,53 @@ hooks in the plugins bundled with Flot. The resize hook is used to be notified after the plot was resized. + - clickHoverFindNearby [phase 7] + ```function (canvasX, canvasY, seriesFilter, distance)``` + + The clickHoverFindNearby hook is used to extend the hover and click + events. It will be called whenever an attempt to determine if an + item has been hovered over or clicked on happens. You are expected + to provide an item or null in return depending on if an item is + being hovered over. + + canvasX and canvasY are the plot-space coordinates that are being + hovered over. Being in plot-space and not canvas-space, these may + be negative as the plot generally does not take up the whole + canvas. + + seriesFilter is a function to determine if the series should be + checked. + + radius is the base distance from an item that you should use + as an area to search in if you aren't searching in strict bounds + + computeDistance is a function to utilize to compute the distance + from the cursor. It may not be provided, so you will still need + to provide your own distance calculation + + items is an array you should push your found items into. It is + unsorted and may hold items from any other plugin or flot's + base calculation. + + The item to be returned should have the following: + + datapoint - The specific bit of information hovered over + + dataindex - The index of the passed in datapoint in a series + + series - The information about the series that was hovered over + + seriesindex - The index of the series hovered over + + distance - Distance, in pixels, between the item and cursor. + If distance is returned as undefined, the item will + be treated as "infinite" distance, and will not be + chosen instead of an item with a distance returned + + Distance is added here to the items previously returned from + 'findNearbyItem' so that we can decide which item should be + returned as 'item' to 'plothover' and 'plotclick' events + - shutdown [phase 8] ```function (plot, eventHolder)``` diff --git a/source/jquery.flot.hover.js b/source/jquery.flot.hover.js index ee9f6d1f..b6fbbe57 100644 --- a/source/jquery.flot.hover.js +++ b/source/jquery.flot.hover.js @@ -154,12 +154,22 @@ the tooltip from webcharts). pos.pageX = page.X; pos.pageY = page.Y; - var item = plot.findNearbyItem(canvasX, canvasY, seriesFilter, distance); + var items = plot.findNearbyItems(canvasX, canvasY, seriesFilter, distance); + var item = items[0]; + + for (var i = 1; i < items.length; ++i) { + if (item.distance === undefined || + items[i].distance < item.distance) { + item = items[i]; + } + } if (item) { // fill in mouse pos for any listeners out there item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left, 10); item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top, 10); + } else { + item = null; } if (options.grid.autoHighlight) { @@ -179,7 +189,7 @@ the tooltip from webcharts). } } - plot.getPlaceholder().trigger(eventname, [pos, item]); + plot.getPlaceholder().trigger(eventname, [pos, item, items]); } function highlight(s, point, auto) { diff --git a/source/jquery.flot.js b/source/jquery.flot.js index 4ae99c70..d8be75f9 100644 --- a/source/jquery.flot.js +++ b/source/jquery.flot.js @@ -234,6 +234,7 @@ Licensed under the MIT license. drawSeries: [], drawAxis: [], draw: [], + clickHoverFindNearby: [], axisReserveSpace: [], bindEvents: [], drawOverlay: [], @@ -345,6 +346,7 @@ Licensed under the MIT license. plot.computeRangeForDataSeries = computeRangeForDataSeries; plot.adjustSeriesDataRange = adjustSeriesDataRange; plot.findNearbyItem = findNearbyItem; + plot.findNearbyItems = findNearbyItems; plot.findNearbyInterpolationPoint = findNearbyInterpolationPoint; plot.computeValuePrecision = computeValuePrecision; plot.computeTickSize = computeTickSize; @@ -2473,10 +2475,33 @@ Licensed under the MIT license. } } - // returns the data item the mouse is over/ the cursor is closest to, or null if none is found + function findNearbyItems(mouseX, mouseY, seriesFilter, radius, computeDistance) { + var items = findItems(mouseX, mouseY, seriesFilter, radius, computeDistance); + executeHooks(hooks.clickHoverFindNearby, [mouseX, mouseY, seriesFilter, radius, computeDistance, items]); + return items; + } + function findNearbyItem(mouseX, mouseY, seriesFilter, radius, computeDistance) { - var i, j, - item = null, + var items = findNearbyItems(mouseX, mouseY, seriesFilter, radius, computeDistance); + var closest = items[0]; + + for (var i = 1; i < items.length; ++i) { + if (closest.distance != undefined && items[i].distance < closest.distance) { + closest = items[i]; + } + } + + if (closest) { + return closest; + } + + return null; + } + + // returns the data item the mouse is over/ the cursor is closest to, or null if none is found + function findItems(mouseX, mouseY, seriesFilter, radius, computeDistance) { + var i, j, k, foundItems = []; + items = [], smallestDistance = radius * radius + 1; for (var i = series.length - 1; i >= 0; --i) { @@ -2489,30 +2514,35 @@ Licensed under the MIT license. var found = findNearbyPoint(s, mouseX, mouseY, radius, smallestDistance, computeDistance); if (found) { smallestDistance = found.distance; - item = [i, found.dataIndex]; + items.push([i, found.dataIndex]); } } if (s.bars.show && !item) { // no other point can be nearby var foundIndex = findNearbyBar(s, mouseX, mouseY); - if (foundIndex) item = [i, foundIndex]; + if (foundIndex) { + items.push([i, foundIndex]); + } } } - if (item) { - i = item[0]; - j = item[1]; - var ps = series[i].datapoints.pointsize; + if (items.length) { + for (k = 0; i < items.length; k++) { + i = item[k][0]; + j = item[k][1]; + var ps = series[i].datapoints.pointsize; - return { - datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), - dataIndex: j, - series: series[i], - seriesIndex: i - }; + foundItems.push({ + datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i, + distance: smallestDistance + }); + } } - return null; + return foundItems; } function findNearbyPoint (series, mouseX, mouseY, maxDistance, smallestDistance, computeDistance) { diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index d98b70c6..66bb8676 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -228,5 +228,80 @@ describe("flot hover plugin", function () { expect(getEntireCanvasData(canvas)).toContainPixelColor(rgba(10, 20, 30, 1)); }); + it('should call hooked function when canvas is hovered over', function() { + var spy = jasmine.createSpy('spy'); + + options.hooks = {clickHoverFindNearby: [spy]}; + plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); + + var eventHolder = plot.getEventHolder(), + offset = plot.getPlotOffset(), + axisx = plot.getXAxes()[0], + axisy = plot.getYAxes()[0], + x = axisx.p2c(2) + offset.left, + y = axisy.p2c(3) + offset.top, + noButton = 0; + + simulate.mouseMove(eventHolder, x, y, noButton); + jasmine.clock().tick(100); + + expect(spy).toHaveBeenCalled(); + }); + + it('should pass item returned from hook in items', function() { + var testItem = {distance: 5}; + var hook = function(_0, _1, _2, _3, _4, _5, items) { + items.push(testItem); + } + + options.hooks = {clickHoverFindNearby: [hook]}; + plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); + + $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { + var seenTestItem = false; + for (const i of items) { + if (i === testItem) { + seenTestItem = true; + } + } + expect(seenTestItem).toBe(true); + }); + + var eventHolder = plot.getEventHolder(), + offset = plot.getPlotOffset(), + axisx = plot.getXAxes()[0], + axisy = plot.getYAxes()[0], + x = axisx.p2c(2) + offset.left, + y = axisy.p2c(3) + offset.top, + noButton = 0; + + simulate.mouseMove(eventHolder, x, y, noButton); + jasmine.clock().tick(100); + }) + + it('should choose closest item from items returned by hooks', function() { + var distance = 5, + testItem = {distance: distance}; + var hook = function(_0, _1, _2, _3, _4, _5, items) { + items.push(testItem); + } + + options.hooks = {clickHoverFindNearby: [hook]}; + + $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { + expect(item.distance < distance).toBe(true); + }); + + var eventHolder = plot.getEventHolder(), + offset = plot.getPlotOffset(), + axisx = plot.getXAxes()[0], + axisy = plot.getYAxes()[0], + x = axisx.p2c(2) + offset.left, + y = axisy.p2c(3) + offset.top, + noButton = 0; + + simulate.mouseMove(eventHolder, x, y, noButton); + jasmine.clock().tick(100); + }); }); }); From e848f0576f729f0a3584f785a66693080ebaabe7 Mon Sep 17 00:00:00 2001 From: andrewdove1 Date: Tue, 11 Jun 2019 08:36:44 -0500 Subject: [PATCH 2/8] eslint fixes --- tests/jquery.flot.hover.Test.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index 66bb8676..dea57092 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -231,10 +231,10 @@ describe("flot hover plugin", function () { it('should call hooked function when canvas is hovered over', function() { var spy = jasmine.createSpy('spy'); - options.hooks = {clickHoverFindNearby: [spy]}; + options.hooks = {clickHoverFindNearby: [spy]}; plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); - var eventHolder = plot.getEventHolder(), + var eventHolder = plot.getEventHolder(), offset = plot.getPlotOffset(), axisx = plot.getXAxes()[0], axisy = plot.getYAxes()[0], @@ -242,7 +242,7 @@ describe("flot hover plugin", function () { y = axisy.p2c(3) + offset.top, noButton = 0; - simulate.mouseMove(eventHolder, x, y, noButton); + simulate.mouseMove(eventHolder, x, y, noButton); jasmine.clock().tick(100); expect(spy).toHaveBeenCalled(); @@ -254,10 +254,10 @@ describe("flot hover plugin", function () { items.push(testItem); } - options.hooks = {clickHoverFindNearby: [hook]}; + options.hooks = {clickHoverFindNearby: [hook]}; plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); - $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { + $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { var seenTestItem = false; for (const i of items) { if (i === testItem) { @@ -267,7 +267,7 @@ describe("flot hover plugin", function () { expect(seenTestItem).toBe(true); }); - var eventHolder = plot.getEventHolder(), + var eventHolder = plot.getEventHolder(), offset = plot.getPlotOffset(), axisx = plot.getXAxes()[0], axisy = plot.getYAxes()[0], @@ -275,7 +275,7 @@ describe("flot hover plugin", function () { y = axisy.p2c(3) + offset.top, noButton = 0; - simulate.mouseMove(eventHolder, x, y, noButton); + simulate.mouseMove(eventHolder, x, y, noButton); jasmine.clock().tick(100); }) @@ -286,13 +286,13 @@ describe("flot hover plugin", function () { items.push(testItem); } - options.hooks = {clickHoverFindNearby: [hook]}; + options.hooks = {clickHoverFindNearby: [hook]}; - $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { + $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { expect(item.distance < distance).toBe(true); }); - var eventHolder = plot.getEventHolder(), + var eventHolder = plot.getEventHolder(), offset = plot.getPlotOffset(), axisx = plot.getXAxes()[0], axisy = plot.getYAxes()[0], @@ -300,7 +300,7 @@ describe("flot hover plugin", function () { y = axisy.p2c(3) + offset.top, noButton = 0; - simulate.mouseMove(eventHolder, x, y, noButton); + simulate.mouseMove(eventHolder, x, y, noButton); jasmine.clock().tick(100); }); }); From ce10f2d50d4787cfca6ce2342f6cd4ead466d105 Mon Sep 17 00:00:00 2001 From: andrewdove1 Date: Tue, 11 Jun 2019 08:37:58 -0500 Subject: [PATCH 3/8] Fix failing test --- source/jquery.flot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/jquery.flot.js b/source/jquery.flot.js index d8be75f9..35ad60f6 100644 --- a/source/jquery.flot.js +++ b/source/jquery.flot.js @@ -2500,7 +2500,7 @@ Licensed under the MIT license. // returns the data item the mouse is over/ the cursor is closest to, or null if none is found function findItems(mouseX, mouseY, seriesFilter, radius, computeDistance) { - var i, j, k, foundItems = []; + var i, j, k, foundItems = [], items = [], smallestDistance = radius * radius + 1; From accf62f8fe3643153cd92230b2457e4f9c84b764 Mon Sep 17 00:00:00 2001 From: andrewdove1 Date: Tue, 11 Jun 2019 13:41:05 -0500 Subject: [PATCH 4/8] eslint fixes --- tests/jquery.flot.hover.Test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index dea57092..d26d76b9 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -245,10 +245,10 @@ describe("flot hover plugin", function () { simulate.mouseMove(eventHolder, x, y, noButton); jasmine.clock().tick(100); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalled(); }); - it('should pass item returned from hook in items', function() { + it('should pass item returned from hook in items', function() { var testItem = {distance: 5}; var hook = function(_0, _1, _2, _3, _4, _5, items) { items.push(testItem); @@ -279,7 +279,7 @@ describe("flot hover plugin", function () { jasmine.clock().tick(100); }) - it('should choose closest item from items returned by hooks', function() { + it('should choose closest item from items returned by hooks', function() { var distance = 5, testItem = {distance: distance}; var hook = function(_0, _1, _2, _3, _4, _5, items) { From 63d5291f171d0cf2ce1d9c1b9fee920186cfbd81 Mon Sep 17 00:00:00 2001 From: andrewdove1 Date: Tue, 11 Jun 2019 14:14:29 -0500 Subject: [PATCH 5/8] Fixed tests --- source/jquery.flot.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/jquery.flot.js b/source/jquery.flot.js index 35ad60f6..207a4c11 100644 --- a/source/jquery.flot.js +++ b/source/jquery.flot.js @@ -2518,7 +2518,7 @@ Licensed under the MIT license. } } - if (s.bars.show && !item) { // no other point can be nearby + if (s.bars.show && !items.length) { // no other point can be nearby var foundIndex = findNearbyBar(s, mouseX, mouseY); if (foundIndex) { items.push([i, foundIndex]); @@ -2527,9 +2527,9 @@ Licensed under the MIT license. } if (items.length) { - for (k = 0; i < items.length; k++) { - i = item[k][0]; - j = item[k][1]; + for (k = 0; k < items.length; k++) { + i = items[k][0]; + j = items[k][1]; var ps = series[i].datapoints.pointsize; foundItems.push({ From ee458dd523a481e53a001cdacff4e505ca2425b2 Mon Sep 17 00:00:00 2001 From: Jonathan Meyer Date: Thu, 31 Oct 2019 14:40:27 -0500 Subject: [PATCH 6/8] Changing name of hook. --- docs/API.md | 14 ++++++++------ source/jquery.flot.js | 4 ++-- tests/jquery.flot.hover.Test.js | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/API.md b/docs/API.md index c3b51bf2..976026b7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1670,14 +1670,16 @@ hooks in the plugins bundled with Flot. The resize hook is used to be notified after the plot was resized. - - clickHoverFindNearby [phase 7] + - findNearbyItems [phase 7] ```function (canvasX, canvasY, seriesFilter, distance)``` - The clickHoverFindNearby hook is used to extend the hover and click - events. It will be called whenever an attempt to determine if an - item has been hovered over or clicked on happens. You are expected - to provide an item or null in return depending on if an item is - being hovered over. + The findNearbyItems hook is used to extend the default behavior of + the flot-provided 'findNearbyItems' function. The hover plugin is + an example of a plugin that leverages this method to return items + that will be highlighted when hovered over. So, as an example, a new + plugin might provide this hook to allow the hover plugin to highlight + points that the flot-provided findNearbyItems method would have + otherwise not found. canvasX and canvasY are the plot-space coordinates that are being hovered over. Being in plot-space and not canvas-space, these may diff --git a/source/jquery.flot.js b/source/jquery.flot.js index 4f188b1d..a9c360bb 100644 --- a/source/jquery.flot.js +++ b/source/jquery.flot.js @@ -234,7 +234,7 @@ Licensed under the MIT license. drawSeries: [], drawAxis: [], draw: [], - clickHoverFindNearby: [], + findNearbyItems: [], axisReserveSpace: [], bindEvents: [], drawOverlay: [], @@ -2496,7 +2496,7 @@ Licensed under the MIT license. function findNearbyItems(mouseX, mouseY, seriesFilter, radius, computeDistance) { var items = findItems(mouseX, mouseY, seriesFilter, radius, computeDistance); - executeHooks(hooks.clickHoverFindNearby, [mouseX, mouseY, seriesFilter, radius, computeDistance, items]); + executeHooks(hooks.findNearbyItems, [mouseX, mouseY, seriesFilter, radius, computeDistance, items]); return items; } diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index d26d76b9..fef97c02 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -1,4 +1,4 @@ -describe("flot hover plugin", function () { +fdescribe("flot hover plugin", function () { var placeholder, plot, options; var rgba = window.colors.rgba; @@ -231,7 +231,7 @@ describe("flot hover plugin", function () { it('should call hooked function when canvas is hovered over', function() { var spy = jasmine.createSpy('spy'); - options.hooks = {clickHoverFindNearby: [spy]}; + options.hooks = {findNearbyItems: [spy]}; plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); var eventHolder = plot.getEventHolder(), @@ -254,7 +254,7 @@ describe("flot hover plugin", function () { items.push(testItem); } - options.hooks = {clickHoverFindNearby: [hook]}; + options.hooks = {findNearbyItems: [hook]}; plot = $.plot(placeholder, [ [ [0, 0], [2, 3], [10, 10] ] ], options); $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { @@ -286,7 +286,7 @@ describe("flot hover plugin", function () { items.push(testItem); } - options.hooks = {clickHoverFindNearby: [hook]}; + options.hooks = {findNearbyItems: [hook]}; $(plot.getPlaceholder()).on('plothover', function(event, pos, item, items) { expect(item.distance < distance).toBe(true); From e5b1e8139df41083e2ab7c88b4e1109dd5608f52 Mon Sep 17 00:00:00 2001 From: Jonathan Meyer Date: Thu, 31 Oct 2019 14:43:39 -0500 Subject: [PATCH 7/8] Update jquery.flot.hover.Test.js Attempting to resolve a merge conflict. --- tests/jquery.flot.hover.Test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index fef97c02..65d640e1 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -228,6 +228,33 @@ fdescribe("flot hover plugin", function () { expect(getEntireCanvasData(canvas)).toContainPixelColor(rgba(10, 20, 30, 1)); }); + + it('should correctly highlight bars with bottom points specified', function() { + options.series.bars = { show: true, barWidth: 0.5 }; + options.series.lines = undefined; + plot = $.plot(placeholder, [ [ [0, 5, 2], [1, 7, 2], [2, 6, 2] ] ], options); + + var eventHolder = plot.getEventHolder(), + canvas = eventHolder, + offset = plot.getPlotOffset(), + axisx = plot.getXAxes()[0], + axisy = plot.getYAxes()[0], + x = axisx.p2c(1.25) + offset.left, + y = axisy.p2c(1) + offset.top, + noButton = 0; + + simulate.mouseMove(eventHolder, x, y, noButton); + jasmine.clock().tick(100); + + expect(getEntireCanvasData(canvas)).not.toContainPixelColor(rgba(10, 20, 30, 1)); + + y = axisy.p2c(4) + offset.top; + simulate.mouseMove(eventHolder, x, y, noButton); + jasmine.clock().tick(100); + + expect(getEntireCanvasData(canvas)).toContainPixelColor(rgba(10, 20, 30, 1)); + }); + it('should call hooked function when canvas is hovered over', function() { var spy = jasmine.createSpy('spy'); From b7c33cae50be88e67a4a2676aff18f141d0f1306 Mon Sep 17 00:00:00 2001 From: Jonathan Meyer Date: Thu, 31 Oct 2019 14:45:24 -0500 Subject: [PATCH 8/8] Update jquery.flot.hover.Test.js Remove 'fdescribe' --- tests/jquery.flot.hover.Test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/jquery.flot.hover.Test.js b/tests/jquery.flot.hover.Test.js index 65d640e1..a5b014a0 100644 --- a/tests/jquery.flot.hover.Test.js +++ b/tests/jquery.flot.hover.Test.js @@ -1,4 +1,4 @@ -fdescribe("flot hover plugin", function () { +describe("flot hover plugin", function () { var placeholder, plot, options; var rgba = window.colors.rgba;