Began to add global toolbar
1
.gitignore
vendored
@ -7,3 +7,4 @@ __pycache__
|
|||||||
log
|
log
|
||||||
client/favicon_package_v0
|
client/favicon_package_v0
|
||||||
client/favicon.ico
|
client/favicon.ico
|
||||||
|
client/favicon192.png_old
|
@ -10,6 +10,14 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style"
|
<meta name="apple-mobile-web-app-status-bar-style"
|
||||||
content="black-translucent" />
|
content="black-translucent" />
|
||||||
<title>SEAWebClient</title>
|
<title>SEAWebClient</title>
|
||||||
|
|
||||||
|
<!-- Components -->
|
||||||
|
|
||||||
|
<script src="components/control/control.js"></script>
|
||||||
|
<script src="components/divider/divider.js"></script>
|
||||||
|
<script src="components/states_indicator/dates/dates.js"></script>
|
||||||
|
<script src="components/states_indicator/live/live.js"></script>
|
||||||
|
|
||||||
<!-- CSS-Files -->
|
<!-- CSS-Files -->
|
||||||
<link rel="stylesheet" href="externalFiles/alertify.css">
|
<link rel="stylesheet" href="externalFiles/alertify.css">
|
||||||
<link rel="stylesheet" href="externalFiles/swiper.min.css">
|
<link rel="stylesheet" href="externalFiles/swiper.min.css">
|
||||||
|
53
client/components/control/control.css
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
.control-global{
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid dimgray;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border 0.25s;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animated:hover{
|
||||||
|
border: 3px solid dimgray;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-global:hover~.tooltiptext{
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transition-delay: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-global-active{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-global-inactive{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltiptext {
|
||||||
|
visibility:hidden;
|
||||||
|
opacity: 0;
|
||||||
|
background-color:gray;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
padding: 2px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
/* Position the tooltip text - see examples below! */
|
||||||
|
width: 130px;
|
||||||
|
height: 20px;
|
||||||
|
left: 100%;
|
||||||
|
top:3px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
}
|
62
client/components/control/control.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
class Control extends HTMLElement{
|
||||||
|
|
||||||
|
constructor(imgSrcMain, imgSrcAlt, tooltipDescription, callbackMain, callbackAlt = undefined){
|
||||||
|
super();
|
||||||
|
this.imgSrcMain = imgSrcMain;
|
||||||
|
this.imgSrcAlt = imgSrcAlt;
|
||||||
|
this.tooltipDescription = tooltipDescription;
|
||||||
|
this.callbackMain = callbackMain;
|
||||||
|
this.callbackAlt = callbackAlt;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeToMain(){
|
||||||
|
this.permute("control-global-alt", "control-global-main");
|
||||||
|
}
|
||||||
|
|
||||||
|
changeToAlt(){
|
||||||
|
this.permute("control-global-main", "control-global-alt");
|
||||||
|
}
|
||||||
|
|
||||||
|
permute(from, to){
|
||||||
|
let fromElm = this.getElementsByClassName(from)[0];
|
||||||
|
let toElm = this.getElementsByClassName(to)[0];
|
||||||
|
|
||||||
|
fromElm.classList.remove("control-global-active");
|
||||||
|
toElm.classList.remove("control-global-inactive");
|
||||||
|
|
||||||
|
fromElm.classList.add("control-global-inactive");
|
||||||
|
toElm.classList.add("control-global-active");
|
||||||
|
|
||||||
|
this.getAnimations()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render();
|
||||||
|
this.getElementsByClassName("control-global-active")[0].onclick = this.callbackMain;
|
||||||
|
if (this.callbackAlt !== undefined){
|
||||||
|
let altElm = this.getElementsByClassName("control-global-inactive")[0];
|
||||||
|
altElm.onclick = this.callbackAlt;
|
||||||
|
altElm.classList.add("animated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/control/control.css">
|
||||||
|
<div class="tooltip">
|
||||||
|
<img class="control-global control-global-main control-global-active animated" src="${this.imgSrcMain}"/>
|
||||||
|
<img class="control-global control-global-alt control-global-inactive" src="${this.imgSrcAlt}"/>
|
||||||
|
<span class="tooltiptext">${this.tooltipDescription}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-control", Control)
|
||||||
|
|
||||||
|
/*
|
||||||
|
connectedCallback : called when first added
|
||||||
|
disconnectedCallback : called when removed
|
||||||
|
attributeChangedCallback : called when attribute changes. attribute to be added in observedAttributes
|
||||||
|
*/
|
13
client/components/divider/divider.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.divider-container{
|
||||||
|
width: 10px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider{
|
||||||
|
width: 2px;
|
||||||
|
height: 80%;
|
||||||
|
background-color:white;
|
||||||
|
}
|
19
client/components/divider/divider.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
class VerticalDivider extends HTMLElement{
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/divider/divider.css">
|
||||||
|
<div class="divider-container">
|
||||||
|
<div class="divider"/>
|
||||||
|
</div> `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-vertical-divider", VerticalDivider)
|
12
client/components/states_indicator/dates/dates.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.dates{
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
/* For text alignement */
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 30px;
|
||||||
|
|
||||||
|
cursor: default;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
}
|
35
client/components/states_indicator/dates/dates.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
class DatesIndicator extends HTMLElement{
|
||||||
|
constructor(oldestTimestamp, newestTimestamp){
|
||||||
|
super();
|
||||||
|
this.oldestDate = this.timestampToString(oldestTimestamp);
|
||||||
|
this.newestDate = this.timestampToString(newestTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestampToString(timestamp){
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
return date.toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(oldestTimestamp, newestTimestamp){
|
||||||
|
this.oldestDate = this.timestampToString(oldestTimestamp);
|
||||||
|
this.newestDate = this.timestampToString(newestTimestamp);
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/states_indicator/dates/dates.css">
|
||||||
|
<div class="dates">
|
||||||
|
${this.oldestDate}
|
||||||
|
->
|
||||||
|
${this.newestDate}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-dates-indicator", DatesIndicator)
|
20
client/components/states_indicator/live/live.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.live{
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
/* For text alignement */
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 30px;
|
||||||
|
|
||||||
|
cursor: default;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enabled{
|
||||||
|
color: lime;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled{
|
||||||
|
color: red;
|
||||||
|
}
|
32
client/components/states_indicator/live/live.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
class LiveStateIndicator extends HTMLElement{
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeToDisable(){
|
||||||
|
let liveElm = this.getElementsByClassName("live")[0];
|
||||||
|
liveElm.classList.remove("enabled");
|
||||||
|
liveElm.classList.add("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
changeToEnable(){
|
||||||
|
let liveElm = this.getElementsByClassName("live")[0];
|
||||||
|
liveElm.classList.remove("disabled");
|
||||||
|
liveElm.classList.add("enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/states_indicator/live/live.css">
|
||||||
|
<div class="live">
|
||||||
|
LIVE
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-live-state-indicator", LiveStateIndicator)
|
@ -19,6 +19,18 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#control_bar{
|
||||||
|
height: 100%;
|
||||||
|
background-color: dimgray;
|
||||||
|
border-top-left-radius: 12px;
|
||||||
|
border-top-right-radius: 12px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
display: flex;
|
||||||
|
column-gap: 5px;
|
||||||
|
margin-left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************/
|
/*********************/
|
||||||
.graphs-container{
|
.graphs-container{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -193,6 +193,93 @@ function strFormat(val, significant_digits=13) {
|
|||||||
return evalue;
|
return evalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dummyCallback = function(){
|
||||||
|
console.log("Dummy callback called");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defining keys for global controls
|
||||||
|
|
||||||
|
let jumpKey = "jump-control";
|
||||||
|
let goToNowKey = "go-to-now-control";
|
||||||
|
|
||||||
|
let zoomInKey = "zoom-in-control";
|
||||||
|
let zoomOutKey = "zoom-out-control";
|
||||||
|
let shiftOlderKey = "shift-older-control";
|
||||||
|
let shiftNewerKey = "shift-newer-control";
|
||||||
|
|
||||||
|
|
||||||
|
let xyKey = "xy-control";
|
||||||
|
let cursorKey = "cursor-control";
|
||||||
|
let legendsKey = "legends-control";
|
||||||
|
|
||||||
|
let globalControls = (function (){
|
||||||
|
let controlsMap = {};
|
||||||
|
|
||||||
|
function loadControls(panel){
|
||||||
|
let controlBar = document.createElement("div");
|
||||||
|
controlBar.id = "control_bar"
|
||||||
|
panel.appendChild(controlBar);
|
||||||
|
|
||||||
|
let jumpControl = new Control("res/jump.png", "res/jump_blocked.png", "Jump", dummyCallback);
|
||||||
|
let goToNowControl = new Control("res/go_to_now.png", "res/go_to_now_blocked.png", "Go to now", dummyCallback);
|
||||||
|
|
||||||
|
let zoomInControl = new Control("res/zoom_in.png", "res/zoom_in_blocked.png", "Zoom in", dummyCallback);
|
||||||
|
let zoomOutControl = new Control("res/zoom_out.png", "res/zoom_out_blocked.png", "Zoon out", dummyCallback);
|
||||||
|
let shiftOlderControl = new Control("res/shift_older.png", "res/shift_older_blocked.png", "Shift to older", dummyCallback);
|
||||||
|
let shiftNewerControl = new Control("res/shift_newer.png", "res/shift_newer_blocked.png", "Shift to newer", dummyCallback);
|
||||||
|
|
||||||
|
|
||||||
|
let xyControl = new Control("res/y_direction.png", "res/x_direction.png", "Time<->Y zoom", dummyCallback, dummyCallback);
|
||||||
|
let cursorControl = new Control("res/remove_cursor.png", "res/remove_cursor_blocked.png", "Remove cursor",dummyCallback);
|
||||||
|
let legendsControl = new Control("res/display_legends.png", "res/remove_legends.png", "Legends", dummyCallback, dummyCallback);
|
||||||
|
|
||||||
|
let now = Date.now();
|
||||||
|
let old = now - 1000*3600;
|
||||||
|
let dates = new DatesIndicator(old, now);
|
||||||
|
let liveState = new LiveStateIndicator();
|
||||||
|
|
||||||
|
controlBar.appendChild(jumpControl)
|
||||||
|
controlBar.appendChild(goToNowControl)
|
||||||
|
goToNowControl.changeToAlt();
|
||||||
|
controlBar.appendChild(new VerticalDivider());
|
||||||
|
controlBar.appendChild(zoomInControl);
|
||||||
|
controlBar.appendChild(zoomOutControl);
|
||||||
|
controlBar.appendChild(shiftOlderControl);
|
||||||
|
controlBar.appendChild(shiftNewerControl);
|
||||||
|
controlBar.appendChild(new VerticalDivider());
|
||||||
|
controlBar.appendChild(xyControl);
|
||||||
|
controlBar.appendChild(cursorControl);
|
||||||
|
cursorControl.changeToAlt();
|
||||||
|
controlBar.appendChild(legendsControl);
|
||||||
|
legendsControl.changeToAlt();
|
||||||
|
|
||||||
|
panel.appendChild(dates)
|
||||||
|
panel.appendChild(liveState);
|
||||||
|
liveState.changeToDisable();
|
||||||
|
liveState.style.marginLeft = "auto"; //sticks element to the right
|
||||||
|
dates.style.marginLeft = "auto";
|
||||||
|
|
||||||
|
controlsMap[jumpKey] = jumpControl;
|
||||||
|
controlsMap[goToNowKey] = goToNowControl;
|
||||||
|
controlsMap[zoomInKey] = zoomInControl;
|
||||||
|
controlsMap[zoomOutKey] = zoomOutControl;
|
||||||
|
controlsMap[shiftOlderKey] = shiftOlderControl;
|
||||||
|
controlsMap[shiftNewerKey] = shiftNewerControl;
|
||||||
|
controlsMap[xyKey] = xyControl;
|
||||||
|
controlsMap[cursorKey] = cursorControl;
|
||||||
|
controlsMap[legendsKey] = legendsControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getControlsMap(){
|
||||||
|
return controlsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadControls: loadControls,
|
||||||
|
getControlsMap: getControlsMap,
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
let graphs = (function (){
|
let graphs = (function (){
|
||||||
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
||||||
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
||||||
@ -837,6 +924,7 @@ let graphs = (function (){
|
|||||||
container.parentNode.querySelector('.panel span').appendChild(zoomMode);
|
container.parentNode.querySelector('.panel span').appendChild(zoomMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The cross to display "main" panel at the location of the graphs
|
||||||
let gotoMainElm = document.createElement('div');
|
let gotoMainElm = document.createElement('div');
|
||||||
gotoMainElm.innerHTML = "×";
|
gotoMainElm.innerHTML = "×";
|
||||||
let currentSwiper = swiper[f];
|
let currentSwiper = swiper[f];
|
||||||
|
BIN
client/res/display_legends.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
client/res/go_to_now.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
client/res/go_to_now_blocked.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
client/res/jump.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
client/res/jump_blocked.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
client/res/remove_cursor.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
client/res/remove_cursor_blocked.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
client/res/remove_legends.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
client/res/shift_newer.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
client/res/shift_newer_blocked.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
client/res/shift_older.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
client/res/shift_older_blocked.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
client/res/x_direction.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
client/res/y_direction.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
client/res/zoom_in.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
client/res/zoom_in_blocked.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
client/res/zoom_out.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
client/res/zoom_out_blocked.png
Normal file
After Width: | Height: | Size: 60 KiB |
@ -38,6 +38,8 @@ def guess_mimetype(filename):
|
|||||||
mimetype = 'text/css'
|
mimetype = 'text/css'
|
||||||
elif filename.endswith('.ico'):
|
elif filename.endswith('.ico'):
|
||||||
mimetype = 'image/x-icon'
|
mimetype = 'image/x-icon'
|
||||||
|
elif filename.endswith(".png"):
|
||||||
|
mimetype = "image/png"
|
||||||
else:
|
else:
|
||||||
mimetype = 'text/html'
|
mimetype = 'text/html'
|
||||||
return mimetype
|
return mimetype
|
||||||
@ -147,11 +149,16 @@ def subdir_test_file(file):
|
|||||||
resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file))
|
resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
@app.route('/components/control/<file>')
|
||||||
|
@app.route('/components/divider/<file>')
|
||||||
|
@app.route('/components/states_indicator/dates/<file>')
|
||||||
|
@app.route('/components/states_indicator/live/<file>')
|
||||||
|
@app.route('/res/<file>')
|
||||||
@app.route('/jsFiles/<file>')
|
@app.route('/jsFiles/<file>')
|
||||||
@app.route('/cssFiles/<file>')
|
@app.route('/cssFiles/<file>')
|
||||||
@app.route('/externalFiles/<file>')
|
@app.route('/externalFiles/<file>')
|
||||||
def subdir_file(file):
|
def subdir_file(file):
|
||||||
subdir = flask.request.path.split('/')[1]
|
subdir = "/".join(flask.request.path.split("/")[1:-1])
|
||||||
resp = flask.send_file("client/" + subdir+"/"+file, mimetype=guess_mimetype(file))
|
resp = flask.send_file("client/" + subdir+"/"+file, mimetype=guess_mimetype(file))
|
||||||
#resp.headers['Content-Security-Policy'] = "sandbox; script-src 'unsafe-inline';"
|
#resp.headers['Content-Security-Policy'] = "sandbox; script-src 'unsafe-inline';"
|
||||||
return resp
|
return resp
|
||||||
|