--- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.selection.js Thu Sep 26 11:55:17 2019 +0200
+++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.selection.js Wed Oct 16 13:00:43 2019 +0200
@@ -6,10 +6,11 @@
The plugin supports these options:
selection: {
- mode: null or "x" or "y" or "xy",
- color: color,
- shape: "round" or "miter" or "bevel",
- minSize: number of pixels
+ mode: null or "x" or "y" or "xy" or "smart",
+ color: color,
+ shape: "round" or "miter" or "bevel",
+ visualization: "fill" or "focus",
+ minSize: number of pixels
}
Selection support is enabled by setting the mode to one of "x", "y" or "xy".
@@ -19,6 +20,12 @@
later on, you can get to it with plot.getOptions().selection.color). "shape"
is the shape of the corners of the selection.
+The way how the selection is visualized, can be changed by using the option
+"visualization". Flot currently supports two modes: "focus" and "fill". The
+option "focus" draws a colored bezel around the selected area while keeping
+the selected area clear. The option "fill" highlights (i.e., fills) the
+selected area with a colored highlight.
+
"minSize" is the minimum size a selection can be in pixels. This value can
be customized to determine the smallest size a selection can be and still
have the selection rectangle be displayed. When customizing this value, the
@@ -33,11 +40,11 @@
the DOM element you passed into the plot function. The event handler gets a
parameter with the ranges selected on the axes, like this:
- placeholder.bind( "plotselected", function( event, ranges ) {
- alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
- // similar for yaxis - with multiple axes, the extra ones are in
- // x2axis, x3axis, ...
- });
+ placeholder.bind( "plotselected", function( event, ranges ) {
+ alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
+ // similar for yaxis - with multiple axes, the extra ones are in
+ // x2axis, x3axis, ...
+ });
The "plotselected" event is only fired when the user has finished making the
selection. A "plotselecting" event is fired during the process with the same
@@ -58,7 +65,7 @@
an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
this:
- setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
+ setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
setSelection will trigger the "plotselected" event when called. If you don't
want that to happen, e.g. if you're inside a "plotselected" handler, pass
@@ -81,10 +88,14 @@
(function ($) {
function init(plot) {
var selection = {
- first: { x: -1, y: -1}, second: { x: -1, y: -1},
- show: false,
- active: false
- };
+ first: {x: -1, y: -1},
+ second: {x: -1, y: -1},
+ show: false,
+ currentMode: 'xy',
+ active: false
+ };
+
+ var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT;
// FIXME: The drag handling implemented here should be
// abstracted out, there's some similar code from a library in
@@ -94,19 +105,23 @@
var savedhandlers = {};
var mouseUpHandler = null;
-
+
function onMouseMove(e) {
if (selection.active) {
updateSelection(e);
-
+
plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
}
}
function onMouseDown(e) {
- if (e.which != 1) // only accept left-click
- return;
-
+ var o = plot.getOptions();
+ // only accept left-click
+ if (e.which !== 1 || o.selection.mode === null) return;
+
+ // reinitialize currentMode
+ selection.currentMode = 'xy';
+
// cancel out any text selections
document.body.focus();
@@ -127,26 +142,29 @@
// this is a bit silly, but we have to use a closure to be
// able to whack the same handler again
mouseUpHandler = function (e) { onMouseUp(e); };
-
+
$(document).one("mouseup", mouseUpHandler);
}
function onMouseUp(e) {
mouseUpHandler = null;
-
+
// revert drag stuff for old-school browsers
- if (document.onselectstart !== undefined)
+ if (document.onselectstart !== undefined) {
document.onselectstart = savedhandlers.onselectstart;
- if (document.ondrag !== undefined)
+ }
+
+ if (document.ondrag !== undefined) {
document.ondrag = savedhandlers.ondrag;
+ }
// no more dragging
selection.active = false;
updateSelection(e);
- if (selectionIsSane())
+ if (selectionIsSane()) {
triggerSelectedEvent();
- else {
+ } else {
// this counts as a clear
plot.getPlaceholder().trigger("plotunselected", [ ]);
plot.getPlaceholder().trigger("plotselecting", [ null ]);
@@ -156,15 +174,27 @@
}
function getSelection() {
- if (!selectionIsSane())
- return null;
-
+ if (!selectionIsSane()) return null;
+
if (!selection.show) return null;
- var r = {}, c1 = selection.first, c2 = selection.second;
+ var r = {},
+ c1 = {x: selection.first.x, y: selection.first.y},
+ c2 = {x: selection.second.x, y: selection.second.y};
+
+ if (selectionDirection(plot) === 'x') {
+ c1.y = 0;
+ c2.y = plot.height();
+ }
+
+ if (selectionDirection(plot) === 'y') {
+ c1.x = 0;
+ c2.x = plot.width();
+ }
+
$.each(plot.getAxes(), function (name, axis) {
if (axis.used) {
- var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
+ var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
}
});
@@ -177,47 +207,77 @@
plot.getPlaceholder().trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future
- if (r.xaxis && r.yaxis)
+ if (r.xaxis && r.yaxis) {
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
+ }
}
function clamp(min, value, max) {
- return value < min ? min: (value > max ? max: value);
+ return value < min ? min : (value > max ? max : value);
+ }
+
+ function selectionDirection(plot) {
+ var o = plot.getOptions();
+
+ if (o.selection.mode === 'smart') {
+ return selection.currentMode;
+ } else {
+ return o.selection.mode;
+ }
+ }
+
+ function updateMode(pos) {
+ if (selection.first) {
+ var delta = {
+ x: pos.x - selection.first.x,
+ y: pos.y - selection.first.y
+ };
+
+ if (Math.abs(delta.x) < SNAPPING_CONSTANT) {
+ selection.currentMode = 'y';
+ } else if (Math.abs(delta.y) < SNAPPING_CONSTANT) {
+ selection.currentMode = 'x';
+ } else {
+ selection.currentMode = 'xy';
+ }
+ }
}
function setSelectionPos(pos, e) {
- var o = plot.getOptions();
var offset = plot.getPlaceholder().offset();
var plotOffset = plot.getPlotOffset();
pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
- if (o.selection.mode == "y")
- pos.x = pos == selection.first ? 0 : plot.width();
+ if (pos !== selection.first) updateMode(pos);
- if (o.selection.mode == "x")
- pos.y = pos == selection.first ? 0 : plot.height();
+ if (selectionDirection(plot) === "y") {
+ pos.x = pos === selection.first ? 0 : plot.width();
+ }
+
+ if (selectionDirection(plot) === "x") {
+ pos.y = pos === selection.first ? 0 : plot.height();
+ }
}
function updateSelection(pos) {
- if (pos.pageX == null)
- return;
+ if (pos.pageX == null) return;
setSelectionPos(selection.second, pos);
if (selectionIsSane()) {
selection.show = true;
plot.triggerRedrawOverlay();
- }
- else
- clearSelection(true);
+ } else clearSelection(true);
}
function clearSelection(preventEvent) {
if (selection.show) {
selection.show = false;
+ selection.currentMode = '';
plot.triggerRedrawOverlay();
- if (!preventEvent)
+ if (!preventEvent) {
plot.getPlaceholder().trigger("plotunselected", [ ]);
+ }
}
}
@@ -227,10 +287,13 @@
for (var k in axes) {
axis = axes[k];
- if (axis.direction == coord) {
+ if (axis.direction === coord) {
key = coord + axis.n + "axis";
- if (!ranges[key] && axis.n == 1)
- key = coord + "axis"; // support x1axis as xaxis
+ if (!ranges[key] && axis.n === 1) {
+ // support x1axis as xaxis
+ key = coord + "axis";
+ }
+
if (ranges[key]) {
from = ranges[key].from;
to = ranges[key].to;
@@ -241,7 +304,7 @@
// backwards-compat stuff - to be removed in future
if (!ranges[key]) {
- axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
+ axis = coord === "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
from = ranges[coord + "1"];
to = ranges[coord + "2"];
}
@@ -252,39 +315,36 @@
from = to;
to = tmp;
}
-
+
return { from: from, to: to, axis: axis };
}
-
+
function setSelection(ranges, preventEvent) {
- var axis, range, o = plot.getOptions();
+ var range;
- if (o.selection.mode == "y") {
+ if (selectionDirection(plot) === "y") {
selection.first.x = 0;
selection.second.x = plot.width();
- }
- else {
+ } else {
range = extractRange(ranges, "x");
-
selection.first.x = range.axis.p2c(range.from);
selection.second.x = range.axis.p2c(range.to);
}
- if (o.selection.mode == "x") {
+ if (selectionDirection(plot) === "x") {
selection.first.y = 0;
selection.second.y = plot.height();
- }
- else {
+ } else {
range = extractRange(ranges, "y");
-
selection.first.y = range.axis.p2c(range.from);
selection.second.y = range.axis.p2c(range.to);
}
selection.show = true;
plot.triggerRedrawOverlay();
- if (!preventEvent && selectionIsSane())
+ if (!preventEvent && selectionIsSane()) {
triggerSelectedEvent();
+ }
}
function selectionIsSane() {
@@ -305,6 +365,88 @@
}
});
+ function drawSelectionDecorations(ctx, x, y, w, h, oX, oY, mode) {
+ var spacing = 3;
+ var fullEarWidth = 15;
+ var earWidth = Math.max(0, Math.min(fullEarWidth, w / 2 - 2, h / 2 - 2));
+ ctx.fillStyle = '#ffffff';
+
+ if (mode === 'xy') {
+ ctx.beginPath();
+ ctx.moveTo(x, y + earWidth);
+ ctx.lineTo(x - 3, y + earWidth);
+ ctx.lineTo(x - 3, y - 3);
+ ctx.lineTo(x + earWidth, y - 3);
+ ctx.lineTo(x + earWidth, y);
+ ctx.lineTo(x, y);
+ ctx.closePath();
+
+ ctx.moveTo(x, y + h - earWidth);
+ ctx.lineTo(x - 3, y + h - earWidth);
+ ctx.lineTo(x - 3, y + h + 3);
+ ctx.lineTo(x + earWidth, y + h + 3);
+ ctx.lineTo(x + earWidth, y + h);
+ ctx.lineTo(x, y + h);
+ ctx.closePath();
+
+ ctx.moveTo(x + w, y + earWidth);
+ ctx.lineTo(x + w + 3, y + earWidth);
+ ctx.lineTo(x + w + 3, y - 3);
+ ctx.lineTo(x + w - earWidth, y - 3);
+ ctx.lineTo(x + w - earWidth, y);
+ ctx.lineTo(x + w, y);
+ ctx.closePath();
+
+ ctx.moveTo(x + w, y + h - earWidth);
+ ctx.lineTo(x + w + 3, y + h - earWidth);
+ ctx.lineTo(x + w + 3, y + h + 3);
+ ctx.lineTo(x + w - earWidth, y + h + 3);
+ ctx.lineTo(x + w - earWidth, y + h);
+ ctx.lineTo(x + w, y + h);
+ ctx.closePath();
+
+ ctx.stroke();
+ ctx.fill();
+ }
+
+ x = oX;
+ y = oY;
+
+ if (mode === 'x') {
+ ctx.beginPath();
+ ctx.moveTo(x, y + fullEarWidth);
+ ctx.lineTo(x, y - fullEarWidth);
+ ctx.lineTo(x - spacing, y - fullEarWidth);
+ ctx.lineTo(x - spacing, y + fullEarWidth);
+ ctx.closePath();
+
+ ctx.moveTo(x + w, y + fullEarWidth);
+ ctx.lineTo(x + w, y - fullEarWidth);
+ ctx.lineTo(x + w + spacing, y - fullEarWidth);
+ ctx.lineTo(x + w + spacing, y + fullEarWidth);
+ ctx.closePath();
+ ctx.stroke();
+ ctx.fill();
+ }
+
+ if (mode === 'y') {
+ ctx.beginPath();
+
+ ctx.moveTo(x - fullEarWidth, y);
+ ctx.lineTo(x + fullEarWidth, y);
+ ctx.lineTo(x + fullEarWidth, y - spacing);
+ ctx.lineTo(x - fullEarWidth, y - spacing);
+ ctx.closePath();
+
+ ctx.moveTo(x - fullEarWidth, y + h);
+ ctx.lineTo(x + fullEarWidth, y + h);
+ ctx.lineTo(x + fullEarWidth, y + h + spacing);
+ ctx.lineTo(x - fullEarWidth, y + h + spacing);
+ ctx.closePath();
+ ctx.stroke();
+ ctx.fill();
+ }
+ }
plot.hooks.drawOverlay.push(function (plot, ctx) {
// draw selection
@@ -316,32 +458,58 @@
ctx.translate(plotOffset.left, plotOffset.top);
var c = $.color.parse(o.selection.color);
+ var visualization = o.selection.visualization;
- ctx.strokeStyle = c.scale('a', 0.8).toString();
+ var scalingFactor = 1;
+
+ // use a dimmer scaling factor if visualization is "fill"
+ if (visualization === "fill") {
+ scalingFactor = 0.8;
+ }
+
+ ctx.strokeStyle = c.scale('a', scalingFactor).toString();
ctx.lineWidth = 1;
ctx.lineJoin = o.selection.shape;
ctx.fillStyle = c.scale('a', 0.4).toString();
var x = Math.min(selection.first.x, selection.second.x) + 0.5,
+ oX = x,
y = Math.min(selection.first.y, selection.second.y) + 0.5,
+ oY = y,
w = Math.abs(selection.second.x - selection.first.x) - 1,
h = Math.abs(selection.second.y - selection.first.y) - 1;
- ctx.fillRect(x, y, w, h);
- ctx.strokeRect(x, y, w, h);
+ if (selectionDirection(plot) === 'x') {
+ h += y;
+ y = 0;
+ }
+
+ if (selectionDirection(plot) === 'y') {
+ w += x;
+ x = 0;
+ }
+
+ if (visualization === "fill") {
+ ctx.fillRect(x, y, w, h);
+ ctx.strokeRect(x, y, w, h);
+ } else {
+ ctx.fillRect(0, 0, plot.width(), plot.height());
+ ctx.clearRect(x, y, w, h);
+ drawSelectionDecorations(ctx, x, y, w, h, oX, oY, selectionDirection(plot));
+ }
ctx.restore();
}
});
-
+
plot.hooks.shutdown.push(function (plot, eventHolder) {
eventHolder.unbind("mousemove", onMouseMove);
eventHolder.unbind("mousedown", onMouseDown);
-
- if (mouseUpHandler)
+
+ if (mouseUpHandler) {
$(document).unbind("mouseup", mouseUpHandler);
+ }
});
-
}
$.plot.plugins.push({
@@ -349,7 +517,8 @@
options: {
selection: {
mode: null, // one of null, "x", "y" or "xy"
- color: "#e8cfac",
+ visualization: "focus", // "focus" or "fill"
+ color: "#888888",
shape: "round", // one of "round", "miter", or "bevel"
minSize: 5 // minimum number of pixels
}