make it possible to draw the legend outside of the plot area

This commit is contained in:
2017-09-30 17:51:33 +02:00
parent d4fd25dc4c
commit 386f211377
9 changed files with 1460 additions and 1402 deletions

View File

@@ -1,68 +1,69 @@
package org.lucares.pdbui;
import org.lucares.pdb.plot.api.AggreateInternal;
import org.lucares.pdb.plot.api.AxisScale;
import org.lucares.pdb.plot.api.Limit;
import org.lucares.pdb.plot.api.PlotSettings;
import org.lucares.pdbui.domain.Aggregate;
import org.lucares.pdbui.domain.LimitBy;
import org.lucares.pdbui.domain.PlotRequest;
import org.lucares.pdbui.domain.YAxis;
class PlotSettingsTransformer {
static PlotSettings toSettings(final PlotRequest request) {
final PlotSettings result = new PlotSettings();
result.setQuery(request.getQuery());
result.setGroupBy(request.getGroupBy());
result.setHeight(request.getHeight());
result.setWidth(request.getWidth());
result.setLimit(request.getLimit());
result.setLimitBy(toLimit(request.getLimitBy()));
result.setDateFrom(request.getDateFrom());
result.setDateRange(request.getDateRange());
result.setYAxisScale(toAxisScale(request.getAxisScale()));
result.setAggregate(toAggregateInternal(request.getAggregate()));
return result;
}
private static AggreateInternal toAggregateInternal(Aggregate aggregate) {
switch (aggregate) {
case NONE:return AggreateInternal.NONE;
case MEAN:return AggreateInternal.MEAN;
}
throw new IllegalStateException("unhandled enum: " + aggregate);
}
private static AxisScale toAxisScale(final YAxis yAxis) {
switch (yAxis) {
case LINEAR:
return AxisScale.LINEAR;
case LOG10:
return AxisScale.LOG10;
case LOG2:
return AxisScale.LOG2;
default:
throw new IllegalStateException("unhandled enum: " + yAxis);
}
}
private static Limit toLimit(final LimitBy limitBy) {
switch (limitBy) {
case NO_LIMIT:
return Limit.NO_LIMIT;
case FEWEST_VALUES:
return Limit.FEWEST_VALUES;
case MOST_VALUES:
return Limit.MOST_VALUES;
case MAX_VALUE:
return Limit.MAX_VALUE;
case MIN_VALUE:
return Limit.MIN_VALUE;
default:
throw new IllegalStateException("unhandled enum: " + limitBy);
}
}
}
package org.lucares.pdbui;
import org.lucares.pdb.plot.api.AggreateInternal;
import org.lucares.pdb.plot.api.AxisScale;
import org.lucares.pdb.plot.api.Limit;
import org.lucares.pdb.plot.api.PlotSettings;
import org.lucares.pdbui.domain.Aggregate;
import org.lucares.pdbui.domain.LimitBy;
import org.lucares.pdbui.domain.PlotRequest;
import org.lucares.pdbui.domain.YAxis;
class PlotSettingsTransformer {
static PlotSettings toSettings(final PlotRequest request) {
final PlotSettings result = new PlotSettings();
result.setQuery(request.getQuery());
result.setGroupBy(request.getGroupBy());
result.setHeight(request.getHeight());
result.setWidth(request.getWidth());
result.setLimit(request.getLimit());
result.setLimitBy(toLimit(request.getLimitBy()));
result.setDateFrom(request.getDateFrom());
result.setDateRange(request.getDateRange());
result.setYAxisScale(toAxisScale(request.getAxisScale()));
result.setAggregate(toAggregateInternal(request.getAggregate()));
result.setKeyOutside(request.isKeyOutside());
return result;
}
private static AggreateInternal toAggregateInternal(Aggregate aggregate) {
switch (aggregate) {
case NONE:return AggreateInternal.NONE;
case MEAN:return AggreateInternal.MEAN;
}
throw new IllegalStateException("unhandled enum: " + aggregate);
}
private static AxisScale toAxisScale(final YAxis yAxis) {
switch (yAxis) {
case LINEAR:
return AxisScale.LINEAR;
case LOG10:
return AxisScale.LOG10;
case LOG2:
return AxisScale.LOG2;
default:
throw new IllegalStateException("unhandled enum: " + yAxis);
}
}
private static Limit toLimit(final LimitBy limitBy) {
switch (limitBy) {
case NO_LIMIT:
return Limit.NO_LIMIT;
case FEWEST_VALUES:
return Limit.FEWEST_VALUES;
case MOST_VALUES:
return Limit.MOST_VALUES;
case MAX_VALUE:
return Limit.MAX_VALUE;
case MIN_VALUE:
return Limit.MIN_VALUE;
default:
throw new IllegalStateException("unhandled enum: " + limitBy);
}
}
}

View File

@@ -1,113 +1,123 @@
package org.lucares.pdbui.domain;
import java.util.List;
public class PlotRequest {
private String query;
private int height = 1000;
private int width = 1000;
private List<String> groupBy;
private LimitBy limitBy = LimitBy.NO_LIMIT;
private YAxis yAxis = YAxis.LINEAR;
private int limit = Integer.MAX_VALUE;
private String dateFrom;
private String dateRange;
private Aggregate aggregate = Aggregate.NONE;
public String getQuery() {
return query;
}
public void setQuery(final String query) {
this.query = query;
}
public int getWidth() {
return width;
}
public void setWidth(final int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(final int height) {
this.height = height;
}
@Override
public String toString() {
return query + ":" + height + "x" + width;
}
public List<String> getGroupBy() {
return groupBy;
}
public void setGroupBy(final List<String> groupBy) {
this.groupBy = groupBy;
}
public LimitBy getLimitBy() {
return limitBy;
}
public void setLimitBy(final LimitBy limitBy) {
this.limitBy = limitBy;
}
public int getLimit() {
return limit;
}
public void setLimit(final int limit) {
this.limit = limit;
}
public String getDateFrom() {
return dateFrom;
}
public void setDateFrom(final String dateFrom) {
this.dateFrom = dateFrom;
}
public String getDateRange() {
return dateRange;
}
public void setDateRange(final String dateRange) {
if (!dateRange.matches("\\d+ (second|minute|hour|day|week|month)s?")) {
throw new IllegalArgumentException(dateRange + " is not a valid range");
}
this.dateRange = dateRange;
}
public YAxis getAxisScale() {
return yAxis;
}
public void setAxisScale(final YAxis yAxis) {
this.yAxis = yAxis;
}
public void setAggregate(Aggregate aggregate) {
this.aggregate = aggregate;
}
public Aggregate getAggregate() {
return aggregate;
}
}
package org.lucares.pdbui.domain;
import java.util.List;
public class PlotRequest {
private String query;
private int height = 1000;
private int width = 1000;
private List<String> groupBy;
private LimitBy limitBy = LimitBy.NO_LIMIT;
private YAxis yAxis = YAxis.LINEAR;
private int limit = Integer.MAX_VALUE;
private String dateFrom;
private String dateRange;
private Aggregate aggregate = Aggregate.NONE;
private boolean keyOutside;
public String getQuery() {
return query;
}
public void setQuery(final String query) {
this.query = query;
}
public int getWidth() {
return width;
}
public void setWidth(final int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(final int height) {
this.height = height;
}
@Override
public String toString() {
return query + ":" + height + "x" + width;
}
public List<String> getGroupBy() {
return groupBy;
}
public void setGroupBy(final List<String> groupBy) {
this.groupBy = groupBy;
}
public LimitBy getLimitBy() {
return limitBy;
}
public void setLimitBy(final LimitBy limitBy) {
this.limitBy = limitBy;
}
public int getLimit() {
return limit;
}
public void setLimit(final int limit) {
this.limit = limit;
}
public String getDateFrom() {
return dateFrom;
}
public void setDateFrom(final String dateFrom) {
this.dateFrom = dateFrom;
}
public String getDateRange() {
return dateRange;
}
public void setDateRange(final String dateRange) {
if (!dateRange.matches("\\d+ (second|minute|hour|day|week|month)s?")) {
throw new IllegalArgumentException(dateRange + " is not a valid range");
}
this.dateRange = dateRange;
}
public YAxis getAxisScale() {
return yAxis;
}
public void setAxisScale(final YAxis yAxis) {
this.yAxis = yAxis;
}
public void setAggregate(Aggregate aggregate) {
this.aggregate = aggregate;
}
public Aggregate getAggregate() {
return aggregate;
}
public void setKeyOutside(boolean keyOutside) {
this.keyOutside = keyOutside;
}
public boolean isKeyOutside() {
return keyOutside;
}
}

View File

@@ -1,117 +1,125 @@
html {
height: 100%;
margin:0;
padding:0;
font-size: 14px;
}
body{
display: grid;
height: 100vh;
margin: 0;
grid:
"search_field logo" auto
"search logo" auto
"navigation navigation" auto
"result result" 1fr
/ 1fr auto;
}
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
#logo {
grid-area: logo;
font-size: 1.2em;
font-weight: bold;
background-color: black;
color: white;
padding: 3px;
}
#search-input-wrapper {
grid-area: search_field;
}
#search-bar {
grid-area: search;
background-color: #aaa;
padding-bottom:3px;
}
#navigation {
grid-area: navigation;
background-color: #aaa;
display: flex;
justify-content: space-between;
}
.autocomplete .active {
background-color: #AAA;
}
.autocomplete, #search-input-wrapper .autocomplete.open {
overflow-y: scroll;
}
#search-input {
box-sizing: border-box;
border: 0;
}
#search-limit-value {
width: 4em;
}
.input_date {
max-width: 10em;
}
#add-filter {
float:right;
}
#button-bar {
text-align: right;
}
#search-submit {
margin-right:3px;
}
#result-view {
grid-area: result;
background: #eee;
margin: 0;
padding: 0;
overflow: hidden;
}
#result-view i {
background: white;
display:inline;
font-style:normal;
line-height: 1.2em;
}
.center
{
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
input:required:invalid {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT1JREFUeNpi/P//PwMpgImBRMACY/x7/uDX39sXt/67cMoDyOVgMjBjYFbV/8kkqcCBrIER5KS/967s+rmkXxzI5wJiRSBm/v8P7NTfHHFFl5mVdIzhGv4+u///x+xmuAlcdXPB9KeqeLgYd3bDU2ZpRRmwH4DOeAI07QXIRKipYPD35184/nn17CO4p/+cOfjl76+/X4GYAYThGn7/g+Mfh/ZZwjUA/aABpJVhpv6+dQUjZP78Z0YEK7OezS2gwltg64GmfTu6i+HL+mUMP34wgvGvL78ZOEysf8M1sGgZvQIqfA1SDAL8iUUMPIFRQLf+AmMQ4DQ0vYYSrL9vXDz2sq9LFsiX4dLRA0t8OX0SHKzi5bXf2HUMBVA0gN356N7p7xdOS3w5fAgcfNxWtn+BJi9gVVBOQfYPQIABABvRq3BwGT3OAAAAAElFTkSuQmCC);
background-position: right top;
background-repeat: no-repeat;
box-shadow: none;
}
html {
height: 100%;
margin:0;
padding:0;
font-size: 14px;
}
body{
display: grid;
height: 100vh;
margin: 0;
grid:
"search_field logo" auto
"search logo" auto
"navigation navigation" auto
"result result" 1fr
/ 1fr auto;
}
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
.group {
display: inline-block;
}
#logo {
grid-area: logo;
font-size: 1.2em;
font-weight: bold;
background-color: black;
color: white;
padding: 3px;
}
#search-input-wrapper {
grid-area: search_field;
}
#search-bar {
grid-area: search;
background-color: #aaa;
padding-bottom:3px;
}
#navigation {
grid-area: navigation;
background-color: #aaa;
display: flex;
justify-content: space-between;
}
.autocomplete .active {
background-color: #AAA;
}
/* scrollbars are nice, but with them an empty autocomplete box is shown
.autocomplete, #search-input-wrapper .autocomplete.open {
overflow-y: scroll;
}
*/
#search-input {
box-sizing: border-box;
border: 0;
}
#search-limit-value {
width: 4em;
}
.input_date {
max-width: 10em;
}
#add-filter {
float:right;
}
#button-bar {
text-align: right;
}
#search-submit {
margin-left:3px;
margin-right:3px;
}
#result-view {
grid-area: result;
background: #eee;
margin: 0;
padding: 0;
overflow: hidden;
}
#result-view i {
background: white;
display:inline;
font-style:normal;
line-height: 1.2em;
}
.center
{
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
input:required:invalid {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT1JREFUeNpi/P//PwMpgImBRMACY/x7/uDX39sXt/67cMoDyOVgMjBjYFbV/8kkqcCBrIER5KS/967s+rmkXxzI5wJiRSBm/v8P7NTfHHFFl5mVdIzhGv4+u///x+xmuAlcdXPB9KeqeLgYd3bDU2ZpRRmwH4DOeAI07QXIRKipYPD35184/nn17CO4p/+cOfjl76+/X4GYAYThGn7/g+Mfh/ZZwjUA/aABpJVhpv6+dQUjZP78Z0YEK7OezS2gwltg64GmfTu6i+HL+mUMP34wgvGvL78ZOEysf8M1sGgZvQIqfA1SDAL8iUUMPIFRQLf+AmMQ4DQ0vYYSrL9vXDz2sq9LFsiX4dLRA0t8OX0SHKzi5bXf2HUMBVA0gN356N7p7xdOS3w5fAgcfNxWtn+BJi9gVVBOQfYPQIABABvRq3BwGT3OAAAAAElFTkSuQmCC);
background-position: right top;
background-repeat: no-repeat;
box-shadow: none;
}

View File

@@ -1,330 +1,331 @@
$(document).ready(function(){
$('#search-submit').click(plot);
renderGroupBy();
updateSearchLimitValue();
$('#search-limit-by').change(updateSearchLimitValue);
$('#nav_left').click(dateLeftShift);
$('#nav_left_half').click(dateHalfLeftShift);
$('#nav_right_half').click(dateHalfRightShift);
$('#nav_right').click(dateRightShift);
$('#zoom_in').click(zoomIn);
$('#zoom_out').click(zoomOut);
AutoComplete({
HttpMethod: "GET",
Delay: 300,
_QueryArg: function() {
var caretIndex = document.getElementById('search-input').selectionStart + 1;
return 'caretIndex=' + caretIndex + '&query';
},
_Pre: function() {
return encodeURI(this.Input.value);
},
_Post: function(response) {
var result = [];
var responseObject = JSON.parse(response);
responseObject['proposals'].forEach(function(item, index){
var proposal = {};
proposal['Label'] = item.value;
proposal['Value'] = item.proposedQuery;
result.push(proposal);
});
console.log(JSON.stringify(result));
return result;
}
});
});
function zoomIn()
{
shiftDate(0.25);
zoom(0.5);
plot();
}
function zoomOut()
{
shiftDate(-0.5);
zoom(2);
plot();
}
function dateLeftShift()
{
shiftDate(-1);
plot();
}
function dateHalfLeftShift()
{
shiftDate(-0.5);
plot();
}
function dateHalfRightShift()
{
shiftDate(0.5);
plot();
}
function dateRightShift()
{
shiftDate(1);
plot();
}
function zoom(factor)
{
if (!$('#search-date-range').is(":invalid")) {
var dateRange = $('#search-date-range').val();
var tokens = dateRange.split(/ +/,2);
if(tokens.length == 2)
{
var value = parseInt(tokens[0]);
var period = tokens[1];
var newValue = value*factor;
while (newValue != Math.round(newValue)){
switch (period) {
case "second":
case "seconds":
if (value == 1) {
// we reached the smallest range
}
else if (value % 2 == 1){
value = value -1;
}
break;
case "minute":
case "minutes":
value = value * 60;
period = "seconds";
break;
case "hour":
case "hours":
value = value * 60;
period = "minutes";
break;
case "day":
case "days":
value = value * 24;
period = "hours";
break;
case "week":
case "weeks":
value = value * 7;
period = "days";
break;
case "month":
case "months":
value = value * 30;
period = "days";
break;
default:
console.log("unhandled value: "+ period);
break;
}
newValue = value*factor
}
$('#search-date-range').val(newValue + " " + period);
}
}
}
function shiftDate(directionalFactor)
{
var dateBefore = Date.parse($('#search-date-from').val());
var newDate = shiftByInterval(dateBefore, directionalFactor);
$('#search-date-from').val(newDate.toString("yyyy-MM-dd HH:mm:ss"));
}
function shiftByInterval(date, directionalFactor)
{
if (!$('#search-date-range').is(":invalid")) {
var dateRange = $('#search-date-range').val();
var tokens = dateRange.split(/ +/,2);
if(tokens.length == 2)
{
var value = parseInt(tokens[0]);
var period = tokens[1];
var config = {};
value = directionalFactor * value;
switch (period) {
case "second":
case "seconds":
config = { seconds: value };
break;
case "minute":
case "minutes":
config = { minutes: value };
break;
case "hour":
case "hours":
config = { minutes: 60*value };
break;
case "day":
case "days":
config = { days: value };
break;
case "week":
case "weeks":
config = { days: 7*value };
break;
case "month":
case "months":
config = { days: 30*value };
break;
default:
break;
}
var newDate = date.add(config);
return newDate;
}
}
return date;
}
function updateSearchLimitValue () {
var optionSelected = $('#search-limit-by').find("option:selected");
var valueSelected = optionSelected.val();
if (valueSelected == "NO_LIMIT"){
$('#search-limit-value').hide();
}else{
$('#search-limit-value').show();
}
}
function renderGroupBy()
{
var request = {};
var success = function(response){
initSearchGroupBy('#search-group-by-1', response);
initSearchGroupBy('#search-group-by-2', response);
initSearchGroupBy('#search-group-by-3', response);
};
var error = function(e) {
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
};
getJson("fields", request, success, error);
}
function initSearchGroupBy(selector, response)
{
$(selector).empty();
var option = new Option("", "");
$(selector).append($(option));
response.forEach(
(item, index) => {
var option = new Option(item, item);
$(selector).append($(option));
}
);
}
function showLoadingIcon()
{
$('#result-view').html("<div class='center'><div class='uil-cube-css' style='-webkit-transform:scale(0.41)'><div /><div></div><div></div><div></div></div></div>");
}
function plot(event){
if(event){
event.preventDefault(); // prevent submit of form which would reload the page
}
showLoadingIcon();
var request = {};
request['query'] = $('#search-input').val();
request['height'] = $('#result-view').height();
request['width'] = $('#result-view').width();
request['groupBy'] = groupBy();
request['limitBy'] = $('#search-limit-by').val();
request['limit'] = parseInt($('#search-limit-value').val());
request['dateFrom'] = $('#search-date-from').val();
request['dateRange'] = $('#search-date-range').val();
request['axisScale'] = $('#search-y-axis-scale').val();
request['aggregate'] = $('#show-aggregate').val();
var success = function(response){
$('#result-view').html('<img src=\"'+response.imageUrls+'" />');
};
var error = function(e) {
if (e.status == 404){
$('#result-view').text("No data points found.");
}
else{
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
}
};
postJson("plots", request, success, error);
}
function groupBy()
{
var result = [];
for (var i = 1; i <= 3; i++)
{
if ($('#search-group-by-'+i).val() != "")
{
result.push($('#search-group-by-'+i).val());
}
}
return result;
}
function postJson(url, requestData, successCallback, errorCallback) {
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(requestData),
contentType: 'application/json'
})
.done(successCallback)
.fail(errorCallback);
}
function getJson(url, requestData, successCallback, errorCallback) {
$.ajax({
type: "GET",
url: url,
data: requestData,
contentType: 'application/json'
})
.done(successCallback)
.fail(errorCallback);
}
$(document).ready(function(){
$('#search-submit').click(plot);
renderGroupBy();
updateSearchLimitValue();
$('#search-limit-by').change(updateSearchLimitValue);
$('#nav_left').click(dateLeftShift);
$('#nav_left_half').click(dateHalfLeftShift);
$('#nav_right_half').click(dateHalfRightShift);
$('#nav_right').click(dateRightShift);
$('#zoom_in').click(zoomIn);
$('#zoom_out').click(zoomOut);
AutoComplete({
HttpMethod: "GET",
Delay: 300,
_QueryArg: function() {
var caretIndex = document.getElementById('search-input').selectionStart + 1;
return 'caretIndex=' + caretIndex + '&query';
},
_Pre: function() {
return encodeURI(this.Input.value);
},
_Post: function(response) {
var result = [];
var responseObject = JSON.parse(response);
responseObject['proposals'].forEach(function(item, index){
var proposal = {};
proposal['Label'] = item.value;
proposal['Value'] = item.proposedQuery;
result.push(proposal);
});
console.log(JSON.stringify(result));
return result;
}
});
});
function zoomIn()
{
shiftDate(0.25);
zoom(0.5);
plot();
}
function zoomOut()
{
shiftDate(-0.5);
zoom(2);
plot();
}
function dateLeftShift()
{
shiftDate(-1);
plot();
}
function dateHalfLeftShift()
{
shiftDate(-0.5);
plot();
}
function dateHalfRightShift()
{
shiftDate(0.5);
plot();
}
function dateRightShift()
{
shiftDate(1);
plot();
}
function zoom(factor)
{
if (!$('#search-date-range').is(":invalid")) {
var dateRange = $('#search-date-range').val();
var tokens = dateRange.split(/ +/,2);
if(tokens.length == 2)
{
var value = parseInt(tokens[0]);
var period = tokens[1];
var newValue = value*factor;
while (newValue != Math.round(newValue)){
switch (period) {
case "second":
case "seconds":
if (value == 1) {
// we reached the smallest range
}
else if (value % 2 == 1){
value = value -1;
}
break;
case "minute":
case "minutes":
value = value * 60;
period = "seconds";
break;
case "hour":
case "hours":
value = value * 60;
period = "minutes";
break;
case "day":
case "days":
value = value * 24;
period = "hours";
break;
case "week":
case "weeks":
value = value * 7;
period = "days";
break;
case "month":
case "months":
value = value * 30;
period = "days";
break;
default:
console.log("unhandled value: "+ period);
break;
}
newValue = value*factor
}
$('#search-date-range').val(newValue + " " + period);
}
}
}
function shiftDate(directionalFactor)
{
var dateBefore = Date.parse($('#search-date-from').val());
var newDate = shiftByInterval(dateBefore, directionalFactor);
$('#search-date-from').val(newDate.toString("yyyy-MM-dd HH:mm:ss"));
}
function shiftByInterval(date, directionalFactor)
{
if (!$('#search-date-range').is(":invalid")) {
var dateRange = $('#search-date-range').val();
var tokens = dateRange.split(/ +/,2);
if(tokens.length == 2)
{
var value = parseInt(tokens[0]);
var period = tokens[1];
var config = {};
value = directionalFactor * value;
switch (period) {
case "second":
case "seconds":
config = { seconds: value };
break;
case "minute":
case "minutes":
config = { minutes: value };
break;
case "hour":
case "hours":
config = { minutes: 60*value };
break;
case "day":
case "days":
config = { days: value };
break;
case "week":
case "weeks":
config = { days: 7*value };
break;
case "month":
case "months":
config = { days: 30*value };
break;
default:
break;
}
var newDate = date.add(config);
return newDate;
}
}
return date;
}
function updateSearchLimitValue () {
var optionSelected = $('#search-limit-by').find("option:selected");
var valueSelected = optionSelected.val();
if (valueSelected == "NO_LIMIT"){
$('#search-limit-value').hide();
}else{
$('#search-limit-value').show();
}
}
function renderGroupBy()
{
var request = {};
var success = function(response){
initSearchGroupBy('#search-group-by-1', response);
initSearchGroupBy('#search-group-by-2', response);
initSearchGroupBy('#search-group-by-3', response);
};
var error = function(e) {
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
};
getJson("fields", request, success, error);
}
function initSearchGroupBy(selector, response)
{
$(selector).empty();
var option = new Option("", "");
$(selector).append($(option));
response.forEach(
(item, index) => {
var option = new Option(item, item);
$(selector).append($(option));
}
);
}
function showLoadingIcon()
{
$('#result-view').html("<div class='center'><div class='uil-cube-css' style='-webkit-transform:scale(0.41)'><div /><div></div><div></div><div></div></div></div>");
}
function plot(event){
if(event){
event.preventDefault(); // prevent submit of form which would reload the page
}
showLoadingIcon();
var request = {};
request['query'] = $('#search-input').val();
request['height'] = $('#result-view').height();
request['width'] = $('#result-view').width();
request['groupBy'] = groupBy();
request['limitBy'] = $('#search-limit-by').val();
request['limit'] = parseInt($('#search-limit-value').val());
request['dateFrom'] = $('#search-date-from').val();
request['dateRange'] = $('#search-date-range').val();
request['axisScale'] = $('#search-y-axis-scale').val();
request['aggregate'] = $('#show-aggregate').val();
request['keyOutside'] = $('#key-outside').is(":checked");
var success = function(response){
$('#result-view').html('<img src=\"'+response.imageUrls+'" />');
};
var error = function(e) {
if (e.status == 404){
$('#result-view').text("No data points found.");
}
else{
$('#result-view').text("FAILED: " + JSON.parse(e.responseText).message);
}
};
postJson("plots", request, success, error);
}
function groupBy()
{
var result = [];
for (var i = 1; i <= 3; i++)
{
if ($('#search-group-by-'+i).val() != "")
{
result.push($('#search-group-by-'+i).val());
}
}
return result;
}
function postJson(url, requestData, successCallback, errorCallback) {
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(requestData),
contentType: 'application/json'
})
.done(successCallback)
.fail(errorCallback);
}
function getJson(url, requestData, successCallback, errorCallback) {
$.ajax({
type: "GET",
url: url,
data: requestData,
contentType: 'application/json'
})
.done(successCallback)
.fail(errorCallback);
}

View File

@@ -1,83 +1,96 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="js/jquery-3.2.0.min.js"></script>
<script type="text/javascript" src="js/search.js"></script>
<script type="text/javascript" src="js/autocomplete.js"></script>
<script type="text/javascript" src="js/date.js"></script>
<link rel="stylesheet" type="text/css" href="css/typography.css">
<link rel="stylesheet" type="text/css" href="css/design.css">
<link rel="stylesheet" type="text/css" href="css/loading.css">
<link rel="stylesheet" type="text/css" href="css/autocomplete.min.css">
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
</head>
<body>
<div id="logo" aria-hidden="true">LOGO</div>
<div id="search-input-wrapper">
<input id="search-input" placeholder="field=value and anotherField=anotherValue" data-autocomplete="autocomplete"
data-autocomplete-empty-message="nothing found" />
</div>
<div id="search-bar">
<form>
<label for="search-group-by-1">Group By:</label>
<select id="search-group-by-1"></select>
<select id="search-group-by-2"></select>
<select id="search-group-by-3"></select>
<label for="search-limit-by">Limit By:</label>
<select id="search-limit-by">
<option value="NO_LIMIT" selected="selected">no limit</option>
<option value="MOST_VALUES">most values</option>
<option value="FEWEST_VALUES">fewest values</option>
<option value="MAX_VALUE">max value</option>
<option value="MIN_VALUE">min value</option>
</select>
<input type="number" id="search-limit-value" name="search-limit-value" min="1" max="1000" value="10"/>
<label for="search-date-from">From Date:</label>
<input id="search-date-from" class="input_date" type="text" value="{{oldestValue}}" required="required" pattern="\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\d|3[0-1]) [0-2]\d:[0-5]\d:[0-5]\d">
<label for="search-date-range">Interval:</label>
<input id="search-date-range" type="text" list="ranges" required="required" value="1 week" pattern="\d+ (second|minute|hour|day|week|month)s?">
<datalist id="ranges">
<option value="60 seconds">
<option value="5 minutes">
<option value="1 hour">
<option value="1 day">
<option value="1 week">
<option value="1 month">
</datalist>
<label for="search-y-axis-scale">Y-Axis:</label>
<select id="search-y-axis-scale">
<option value="LINEAR" selected="selected">linear</option>
<option value="LOG10">log 10</option>
<option value="LOG2">log 2</option>
</select>
<label for="show-aggregate">Aggregate:</label>
<select id="show-aggregate">
<option value="NONE" selected="selected">-</option>
<option value="MEAN">Mean</option>
</select>
<button id="search-submit"><i class="fa fa-area-chart" aria-hidden="true"></i> Plot</button>
</form>
</div>
<div id="navigation">
<button id="nav_left"><i class="fa fa-angle-double-left" aria-hidden="true"></i></button>
<button id="nav_left_half"><i class="fa fa-angle-left" aria-hidden="true"></i></button>
<div>
<button id="zoom_in"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
<button id="zoom_out"><i class="fa fa-minus-circle" aria-hidden="true"></i></button>
</div>
<button id="nav_right_half"><i class="fa fa-angle-right" aria-hidden="true"></i></button>
<button id="nav_right"><i class="fa fa-angle-double-right" aria-hidden="true"></i></button>
</div>
<div id="result-view">
</div>
</body>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="js/jquery-3.2.0.min.js"></script>
<script type="text/javascript" src="js/search.js"></script>
<script type="text/javascript" src="js/autocomplete.js"></script>
<script type="text/javascript" src="js/date.js"></script>
<link rel="stylesheet" type="text/css" href="css/typography.css">
<link rel="stylesheet" type="text/css" href="css/design.css">
<link rel="stylesheet" type="text/css" href="css/loading.css">
<link rel="stylesheet" type="text/css" href="css/autocomplete.min.css">
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
</head>
<body>
<div id="logo" aria-hidden="true">LOGO</div>
<div id="search-input-wrapper">
<input id="search-input" placeholder="field=value and anotherField=anotherValue" data-autocomplete="autocomplete"
data-autocomplete-empty-message="nothing found" />
</div>
<div id="search-bar">
<form>
<div id="search-settings-bar">
<div class="group">
<label for="search-group-by-1">Group By:</label>
<select id="search-group-by-1"></select>
<select id="search-group-by-2"></select>
<select id="search-group-by-3"></select>
</div>
<div class="group">
<label for="search-limit-by">Limit By:</label>
<select id="search-limit-by">
<option value="NO_LIMIT" selected="selected">no limit</option>
<option value="MOST_VALUES">most values</option>
<option value="FEWEST_VALUES">fewest values</option>
<option value="MAX_VALUE">max value</option>
<option value="MIN_VALUE">min value</option>
</select>
<input type="number" id="search-limit-value" name="search-limit-value" min="1" max="1000" value="10"/>
</div>
<div class="group">
<label for="search-date-from">From Date:</label>
<input id="search-date-from" class="input_date" type="text" value="{{oldestValue}}" required="required" pattern="\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\d|3[0-1]) [0-2]\d:[0-5]\d:[0-5]\d">
</div>
<div class="group">
<label for="search-date-range">Interval:</label>
<input id="search-date-range" type="text" list="ranges" required="required" value="1 week" pattern="\d+ (second|minute|hour|day|week|month)s?">
<datalist id="ranges">
<option value="60 seconds">
<option value="5 minutes">
<option value="1 hour">
<option value="1 day">
<option value="1 week">
<option value="1 month">
</datalist>
</div>
<div class="group">
<label for="search-y-axis-scale">Y-Axis:</label>
<select id="search-y-axis-scale">
<option value="LINEAR" selected="selected">linear</option>
<option value="LOG10">log 10</option>
<option value="LOG2">log 2</option>
</select>
</div>
<div class="group">
<label for="show-aggregate">Aggregate:</label>
<select id="show-aggregate">
<option value="NONE" selected="selected">-</option>
<option value="MEAN">Mean</option>
</select>
</div>
<div class="group">
<input type="checkbox" id="key-outside" />
<label for="key-outside">Legend outside</label>
</div>
<button id="search-submit"><i class="fa fa-area-chart" aria-hidden="true"></i> Plot</button>
</div>
</form>
</div>
<div id="navigation">
<button id="nav_left"><i class="fa fa-angle-double-left" aria-hidden="true"></i></button>
<button id="nav_left_half"><i class="fa fa-angle-left" aria-hidden="true"></i></button>
<div>
<button id="zoom_in"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
<button id="zoom_out"><i class="fa fa-minus-circle" aria-hidden="true"></i></button>
</div>
<button id="nav_right_half"><i class="fa fa-angle-right" aria-hidden="true"></i></button>
<button id="nav_right"><i class="fa fa-angle-double-right" aria-hidden="true"></i></button>
</div>
<div id="result-view">
</div>
</body>
</html>