diff --git a/pdb-ui/src/main/resources/resources/css/design.css b/pdb-ui/src/main/resources/resources/css/design.css index deae86e..d0b771f 100644 --- a/pdb-ui/src/main/resources/resources/css/design.css +++ b/pdb-ui/src/main/resources/resources/css/design.css @@ -90,9 +90,16 @@ textarea { position:relative; } -#result-view { +#result-view img{ + -webkit-user-drag: none; /* prevent Chrome's default behavior, when dragging on an image */ } +#zoom-in-slider { + background: #ccc; + opacity:0.4; +} + + #filter-bar { grid-area: filter_bar; } diff --git a/pdb-ui/src/main/resources/resources/js/ui.js b/pdb-ui/src/main/resources/resources/js/ui.js index e523f47..4dd7764 100644 --- a/pdb-ui/src/main/resources/resources/js/ui.js +++ b/pdb-ui/src/main/resources/resources/js/ui.js @@ -13,6 +13,11 @@ function ffHasParentWithId(el, id) { window.onload=function(){ +const gnuplotLMargin = 110; // The left margin configured for gnuplot +const gnuplotRMargin = 110; // The right margin configured for gnuplot +const gnuplotTMargin = 57; // The top margin configured for gnuplot +const gnuplotBMargin = 76; // The bottom margin configured for gnuplot + Vue.config.keyCodes.arrowUp = 38; Vue.config.keyCodes.arrowDown = 40; @@ -181,6 +186,14 @@ Vue.component('proposal-item', { Vue.component('result-view', { props: ['searchBar', 'resultView'], + data: function() { + return { + in_drag_mode: false, + drag_start_x: 0, + drag_end_x: 0, + zoomInSliderStyle: "display: none;", + }; + }, methods: { prev_image: function() { @@ -199,6 +212,116 @@ Vue.component('result-view', { splitBy['index'] = (splitBy['index']+1) % splitBy['values'].length; plotCurrent(); } + }, + isInPlot: function(x){ + + const imageWith = Math.floor($('#result').width()); + return x > gnuplotLMargin && x < imageWith - gnuplotRMargin; + }, + drag_start: function(event) { + if (event.buttons == 1 && this.isInPlot(event.x) && !data.searchBar.imageLastUsedParams.keyOutside) { + //console.log("drag-start " +event.x+ " " + event.buttons); + this.in_drag_mode = true; + this.drag_start_x = event.x; + this.drag_end_x = event.x; + } + }, + dragging: function(event) { + if (this.in_drag_mode && event.buttons == 1 && this.isInPlot(event.x) && !data.searchBar.imageLastUsedParams.keyOutside){ + //console.log("dragging " + event.layerX+ " " + event.x + " button: " + event.buttons); + this.drag_end_x = event.x; + + const left = this.drag_start_x < this.drag_end_x ? this.drag_start_x : this.drag_end_x; + const width = Math.abs(this.drag_start_x - this.drag_end_x); + + this.zoomInSliderStyle="position: absolute; left: "+left+"px; width: "+width+"px; top:"+gnuplotTMargin+"px; bottom: "+gnuplotBMargin+"px;"; + //console.log(this.zoomInSliderStyle); + } + }, + drag_stop: function(event) { + if (this.in_drag_mode && !data.searchBar.imageLastUsedParams.keyOutside){ + this.in_drag_mode = false; + this.zoomInSliderStyle="display: none;"; + + // Zoom in if the selected area has some arbitrary minimal size + if (Math.abs(this.drag_start_x - this.drag_end_x) > 10) { + const dateFrom = data.searchBar.imageLastUsedParams.dateFrom; + const dateRange = data.searchBar.imageLastUsedParams.dateRange; + + + const startPxInImage = Math.min(this.drag_start_x, this.drag_end_x); + const endPxInImage = Math.max(this.drag_start_x, this.drag_end_x); + + const imageWidth = Math.floor($('#result').width()); + const widthPlotArea = imageWidth - gnuplotLMargin - gnuplotRMargin; + const startPxWithinPlotArea = startPxInImage - gnuplotLMargin; + const endPxWithinPlotArea = endPxInImage - gnuplotLMargin; + + const startPercentOfDateRange = startPxWithinPlotArea / widthPlotArea; + const intervalPercentOfDateRange = (endPxWithinPlotArea- startPxWithinPlotArea) / widthPlotArea; + + const dateRangeInSeconds = this.dateRangeToSeconds(dateRange); + const newStartDate = this.shiftDateBySeconds(dateFrom, dateRangeInSeconds * startPercentOfDateRange); + const newDateRangeInSeconds = Math.max(60, dateRangeInSeconds * intervalPercentOfDateRange); + const newDateRange = Math.ceil(newDateRangeInSeconds) + " seconds"; + + console.log("new range: "+newStartDate+" with interval "+newDateRange); + data.searchBar.dateFrom = newStartDate; + data.searchBar.dateRange = newDateRange; + plotCurrent(); + } + } + }, + drag_abort: function() { + this.in_drag_mode = false; + this.drag_start_x = 0; + this.drag_end_x = 0; + this.zoomInSliderStyle="display: none;"; + }, + shiftDateBySeconds: function(date, shiftInSeconds) { + var oldDate = Date.parse(date); + var newDate = oldDate.add({seconds: shiftInSeconds}); + return newDate.toString("yyyy-MM-dd HH:mm"); + }, + dateRangeToSeconds: function(dateRange){ + var tokens = dateRange.split(/ +/,2); + + var newValue = -1; + if(tokens.length == 2) + { + var value = parseInt(tokens[0]); + var period = tokens[1]; + switch (period) { + case "second": + case "seconds": + newValue = value; + break; + case "minute": + case "minutes": + newValue = value * 60; + break; + case "hour": + case "hours": + newValue = value * 60*60; + break; + case "day": + case "days": + newValue = value * 24*60*60; + break; + case "week": + case "weeks": + newValue = value * 7*24*60*60; + break; + case "month": + case "months": + newValue = value * 30*7*24*60*60; + break; + default: + console.log("unhandled value: "+ period); + break; + } + } + return newValue; } }, computed: { @@ -207,7 +330,17 @@ Vue.component('result-view', { } }, template: ` -
+
+
@@ -910,7 +1043,8 @@ var data = { values: [], index: 0 }, - imagelink: "" + imagelink: "", + imageLastUsedParams: {} }, resultView: { imageUrl: '', @@ -1075,6 +1209,7 @@ function updateImageLink(query) { }; data.searchBar.imagelink = window.location.origin+ window.location.pathname + "plots?" + jQuery.param(params); + data.searchBar.imageLastUsedParams = params; } function createGallery(vm){