diff --git a/.gitignore b/.gitignore
index b53f82c..d438ac3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ __pycache__
.idea
log
client/favicon_package_v0
-client/favicon.ico
\ No newline at end of file
+client/favicon.ico
+client/favicon192.png_old
\ No newline at end of file
diff --git a/client/SEAWebClient.html b/client/SEAWebClient.html
index 6089df5..7eaf6ef 100644
--- a/client/SEAWebClient.html
+++ b/client/SEAWebClient.html
@@ -10,6 +10,14 @@
SEAWebClient
+
+
+
+
+
+
+
+
diff --git a/client/components/control/control.css b/client/components/control/control.css
new file mode 100644
index 0000000..702b3c7
--- /dev/null
+++ b/client/components/control/control.css
@@ -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;
+}
\ No newline at end of file
diff --git a/client/components/control/control.js b/client/components/control/control.js
new file mode 100644
index 0000000..0f189c2
--- /dev/null
+++ b/client/components/control/control.js
@@ -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 = `
+
+
+ `;
+
+ }
+}
+
+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
+*/
\ No newline at end of file
diff --git a/client/components/divider/divider.css b/client/components/divider/divider.css
new file mode 100644
index 0000000..22989d8
--- /dev/null
+++ b/client/components/divider/divider.css
@@ -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;
+}
\ No newline at end of file
diff --git a/client/components/divider/divider.js b/client/components/divider/divider.js
new file mode 100644
index 0000000..9acce8b
--- /dev/null
+++ b/client/components/divider/divider.js
@@ -0,0 +1,19 @@
+class VerticalDivider extends HTMLElement{
+ constructor(){
+ super();
+ }
+
+ connectedCallback(){
+ this.render()
+ }
+
+ render(){
+ this.innerHTML = `
+
+ `
+ }
+}
+
+customElements.define("sea-vertical-divider", VerticalDivider)
diff --git a/client/components/states_indicator/dates/dates.css b/client/components/states_indicator/dates/dates.css
new file mode 100644
index 0000000..c5ba240
--- /dev/null
+++ b/client/components/states_indicator/dates/dates.css
@@ -0,0 +1,12 @@
+.dates{
+ height: 30px;
+
+ /* For text alignement */
+ text-align: center;
+ vertical-align: middle;
+ line-height: 30px;
+
+ cursor: default;
+ color: white;
+
+}
\ No newline at end of file
diff --git a/client/components/states_indicator/dates/dates.js b/client/components/states_indicator/dates/dates.js
new file mode 100644
index 0000000..543d59e
--- /dev/null
+++ b/client/components/states_indicator/dates/dates.js
@@ -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 = `
+
+
+ ${this.oldestDate}
+ ->
+ ${this.newestDate}
+
+ `
+ }
+}
+
+customElements.define("sea-dates-indicator", DatesIndicator)
\ No newline at end of file
diff --git a/client/components/states_indicator/live/live.css b/client/components/states_indicator/live/live.css
new file mode 100644
index 0000000..7818f98
--- /dev/null
+++ b/client/components/states_indicator/live/live.css
@@ -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;
+}
\ No newline at end of file
diff --git a/client/components/states_indicator/live/live.js b/client/components/states_indicator/live/live.js
new file mode 100644
index 0000000..94cf95c
--- /dev/null
+++ b/client/components/states_indicator/live/live.js
@@ -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 = `
+
+
+ LIVE
+
+ `
+ }
+}
+
+customElements.define("sea-live-state-indicator", LiveStateIndicator)
\ No newline at end of file
diff --git a/client/cssFiles/SEAWebClientGraphics.css b/client/cssFiles/SEAWebClientGraphics.css
index 1dd1a81..2760d91 100644
--- a/client/cssFiles/SEAWebClientGraphics.css
+++ b/client/cssFiles/SEAWebClientGraphics.css
@@ -19,6 +19,18 @@
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{
height: 100%;
diff --git a/client/jsFiles/SEAWebClientGraphics.js b/client/jsFiles/SEAWebClientGraphics.js
index 5f9df6f..e6fbc89 100644
--- a/client/jsFiles/SEAWebClientGraphics.js
+++ b/client/jsFiles/SEAWebClientGraphics.js
@@ -193,6 +193,93 @@ function strFormat(val, significant_digits=13) {
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 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=[];
@@ -837,6 +924,7 @@ let graphs = (function (){
container.parentNode.querySelector('.panel span').appendChild(zoomMode);
}
+ // The cross to display "main" panel at the location of the graphs
let gotoMainElm = document.createElement('div');
gotoMainElm.innerHTML = "×";
let currentSwiper = swiper[f];
diff --git a/client/res/display_legends.png b/client/res/display_legends.png
new file mode 100644
index 0000000..6496116
Binary files /dev/null and b/client/res/display_legends.png differ
diff --git a/client/res/go_to_now.png b/client/res/go_to_now.png
new file mode 100644
index 0000000..fe7940a
Binary files /dev/null and b/client/res/go_to_now.png differ
diff --git a/client/res/go_to_now_blocked.png b/client/res/go_to_now_blocked.png
new file mode 100644
index 0000000..c9714af
Binary files /dev/null and b/client/res/go_to_now_blocked.png differ
diff --git a/client/res/jump.png b/client/res/jump.png
new file mode 100644
index 0000000..8e05956
Binary files /dev/null and b/client/res/jump.png differ
diff --git a/client/res/jump_blocked.png b/client/res/jump_blocked.png
new file mode 100644
index 0000000..eb37329
Binary files /dev/null and b/client/res/jump_blocked.png differ
diff --git a/client/res/remove_cursor.png b/client/res/remove_cursor.png
new file mode 100644
index 0000000..953bba4
Binary files /dev/null and b/client/res/remove_cursor.png differ
diff --git a/client/res/remove_cursor_blocked.png b/client/res/remove_cursor_blocked.png
new file mode 100644
index 0000000..d5d1242
Binary files /dev/null and b/client/res/remove_cursor_blocked.png differ
diff --git a/client/res/remove_legends.png b/client/res/remove_legends.png
new file mode 100644
index 0000000..94e7b4f
Binary files /dev/null and b/client/res/remove_legends.png differ
diff --git a/client/res/shift_newer.png b/client/res/shift_newer.png
new file mode 100644
index 0000000..a6500c6
Binary files /dev/null and b/client/res/shift_newer.png differ
diff --git a/client/res/shift_newer_blocked.png b/client/res/shift_newer_blocked.png
new file mode 100644
index 0000000..1a28e34
Binary files /dev/null and b/client/res/shift_newer_blocked.png differ
diff --git a/client/res/shift_older.png b/client/res/shift_older.png
new file mode 100644
index 0000000..28af88b
Binary files /dev/null and b/client/res/shift_older.png differ
diff --git a/client/res/shift_older_blocked.png b/client/res/shift_older_blocked.png
new file mode 100644
index 0000000..8cde5e4
Binary files /dev/null and b/client/res/shift_older_blocked.png differ
diff --git a/client/res/x_direction.png b/client/res/x_direction.png
new file mode 100644
index 0000000..f25141f
Binary files /dev/null and b/client/res/x_direction.png differ
diff --git a/client/res/y_direction.png b/client/res/y_direction.png
new file mode 100644
index 0000000..9cf2784
Binary files /dev/null and b/client/res/y_direction.png differ
diff --git a/client/res/zoom_in.png b/client/res/zoom_in.png
new file mode 100644
index 0000000..228f626
Binary files /dev/null and b/client/res/zoom_in.png differ
diff --git a/client/res/zoom_in_blocked.png b/client/res/zoom_in_blocked.png
new file mode 100644
index 0000000..2648b1f
Binary files /dev/null and b/client/res/zoom_in_blocked.png differ
diff --git a/client/res/zoom_out.png b/client/res/zoom_out.png
new file mode 100644
index 0000000..fa083f7
Binary files /dev/null and b/client/res/zoom_out.png differ
diff --git a/client/res/zoom_out_blocked.png b/client/res/zoom_out_blocked.png
new file mode 100644
index 0000000..1ac73cd
Binary files /dev/null and b/client/res/zoom_out_blocked.png differ
diff --git a/seaweb.py b/seaweb.py
index 051340d..7bfcd98 100755
--- a/seaweb.py
+++ b/seaweb.py
@@ -38,6 +38,8 @@ def guess_mimetype(filename):
mimetype = 'text/css'
elif filename.endswith('.ico'):
mimetype = 'image/x-icon'
+ elif filename.endswith(".png"):
+ mimetype = "image/png"
else:
mimetype = 'text/html'
return mimetype
@@ -147,11 +149,16 @@ def subdir_test_file(file):
resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file))
return resp
+@app.route('/components/control/')
+@app.route('/components/divider/')
+@app.route('/components/states_indicator/dates/')
+@app.route('/components/states_indicator/live/')
+@app.route('/res/')
@app.route('/jsFiles/')
@app.route('/cssFiles/')
@app.route('/externalFiles/')
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.headers['Content-Security-Policy'] = "sandbox; script-src 'unsafe-inline';"
return resp