From 9c0c450920ae3ef86ff609af6ddf86a8266d449f Mon Sep 17 00:00:00 2001
From: Goutte <antoine.goutenoir@gmail.com>
Date: Fri, 30 Jun 2017 16:51:33 +0200
Subject: [PATCH] Add a loader to the whole page.

---
 CHANGELOG.md              |  10 +++++-----
 web/static/js/swapp.js    |  27 +++++++++++++++++++++------
 web/static/js/swapp.ls    |  22 +++++++++++++++-------
 web/view/home.html.jinja2 | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 154 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad8d275..571e6af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,16 +1,16 @@
-## TODO
+## Misc
 
-- Support multiple models for each target
-- Play button to start time dimension
+- [ ] Support multiple models for each target
+- [ ] Play button to start time dimension (not very useful?)
 
 ## 1.0.0
 
 - [x] Cache generated CSVs on the server side
 - [x] Cache generated CSVs on the client side
-- [x] Visits counter
+- [x] Count visits
 - [x] Zoom in on time series
+- [x] Loader and targets loading animation
 - [ ] Start/Stop datetime fields
-- [ ] Loader
 - [ ] Download raw data (as CSV) for current time interval and targets
 - [ ] Same via SAMP
 - [ ] Credit the author of the pixel art planets
diff --git a/web/static/js/swapp.js b/web/static/js/swapp.js
index ffd08dd..aab9d90 100644
--- a/web/static/js/swapp.js
+++ b/web/static/js/swapp.js
@@ -60,7 +60,8 @@
           console.info("Loaded CSV data of " + target.name + ".");
           this$.createTimeSeries(target, data);
           this$.orbits.initOrbiter(target.slug, target.config, data['hci']);
-          return targetButton.removeClass('loading');
+          targetButton.removeClass('loading');
+          return this$.hideLoader();
         }, function(error){
           return console.error('Failed to load CSV data.', error);
         });
@@ -117,6 +118,9 @@
         return ts.resize();
       });
     };
+    SpaceWeather.prototype.hideLoader = function(){
+      return $("#plots_loader").hide();
+    };
     SpaceWeather.prototype.loadData = function(target_slug, started_at, stopped_at){
       "Load the data as CSV for the specified target and interval,\nand return it in a Promise.";
       var sw, promise;
@@ -172,27 +176,38 @@
       });
       return timeSeries.forEach(function(ts){
         ts.options['onMouseOver'] = function(){
-          return timeSeries.forEach(function(ts2){
+          timeSeries.forEach(function(ts2){
             return ts2.showCursor();
           });
+          return true;
         };
         ts.options['onMouseOut'] = function(){
-          return timeSeries.forEach(function(ts2){
+          timeSeries.forEach(function(ts2){
             return ts2.hideCursor();
           });
+          return true;
         };
         ts.options['onMouseMove'] = function(t){
           var ref$;
           timeSeries.forEach(function(ts2){
             return ts2.moveCursor(t);
           });
-          return (ref$ = this$.orbits) != null ? ref$.moveToDate(t) : void 8;
+          if ((ref$ = this$.orbits) != null) {
+            ref$.moveToDate(t);
+          }
+          return true;
         };
         ts.options['onBrushEnd'] = function(sta, sto){
-          return this$.resizeDomain(sta, sto);
+          this$.resizeDomain(sta, sto);
+          return true;
         };
         return ts.options['onDblClick'] = function(){
-          return this$.resetZoom();
+          var ref$;
+          this$.resetZoom();
+          if ((ref$ = $("#zoom_controls_help")) != null) {
+            ref$.remove();
+          }
+          return true;
         };
       });
     };
diff --git a/web/static/js/swapp.ls b/web/static/js/swapp.ls
index 7df4ce2..da0f325 100644
--- a/web/static/js/swapp.ls
+++ b/web/static/js/swapp.ls
@@ -11,6 +11,10 @@
 # templates, such as `home.html.jinja2`.
 
 # Note: We use Promises and ES6 whenever relevant.
+# You also will need d3js v4 documentation : https://d3js.org/
+# We're using a custom build of 4.9.1, one line changed, see d3-custom.js
+# Event bubbling cannot trigger two rects unless we make an event dispatcher,
+# and d3's brush is stopping propagation, as it should by default.
 
 ###############################################################################
 
@@ -81,6 +85,7 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
           @createTimeSeries(target, data)
           @orbits.initOrbiter(target.slug, target.config, data['hci'])
           targetButton.removeClass('loading')
+          @hideLoader()
         ,
         (error) -> console.error('Failed to load CSV data.', error)
       )
@@ -117,6 +122,9 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
     @orbits?.resize();
     timeSeries.forEach((ts) -> ts.resize())
 
+  hideLoader: ->
+    $("\#plots_loader").hide();
+
   loadData: (target_slug, started_at, stopped_at) ->
     """
     Load the data as CSV for the specified target and interval,
@@ -145,7 +153,7 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
     )
     promise
 
-  timeSeries = []  # Not sure why this ain't an instance prop. Probably should.
+  timeSeries = []  # deprecated (was for scoping) ; use @property with ~>
   createTimeSeries: (target, data) ->
     @configuration['parameters'].forEach((parameter) ~>
       container = @configuration['time_series_container']
@@ -155,18 +163,18 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
         id, title, target, data[id], @parameters[id].active, container
       ))
     )
-    timeSeries.forEach((ts) ~>
+    timeSeries.forEach((ts) ~>  # returning true may be faster
       ts.options['onMouseOver'] = ->
-        timeSeries.forEach((ts2) -> ts2.showCursor())
+        timeSeries.forEach((ts2) -> ts2.showCursor()) ; true
       ts.options['onMouseOut'] = ->
-        timeSeries.forEach((ts2) -> ts2.hideCursor())
+        timeSeries.forEach((ts2) -> ts2.hideCursor()) ; true
       ts.options['onMouseMove'] = (t) ~>
         timeSeries.forEach((ts2) -> ts2.moveCursor(t))
-        @orbits?.moveToDate(t)
+        @orbits?.moveToDate(t) ; true
       ts.options['onBrushEnd'] = (sta, sto) ~>
-        @resizeDomain(sta, sto)
+        @resizeDomain(sta, sto) ; true
       ts.options['onDblClick'] = ~>
-        @resetZoom()
+        @resetZoom() ; $("\#zoom_controls_help")?.remove() ; true
     )
 
   enableParameter: (parameter_slug) ->
diff --git a/web/view/home.html.jinja2 b/web/view/home.html.jinja2
index fba8f9f..00948b0 100755
--- a/web/view/home.html.jinja2
+++ b/web/view/home.html.jinja2
@@ -14,6 +14,16 @@
     >
 {%- endmacro %}
 
+
+<div id="plots_loader">
+  <p class="loader-text">Loading Heliopropa&hellip;<br>This might take a while.</p>
+  <div id="plots_loader_img1" class="img"></div>
+  <div id="plots_loader_img2" class="img"></div>
+  <div id="plots_loader_img3" class="img"></div>
+  <div id="plots_loader_img4" class="img"></div>
+  <div id="plots_loader_img5" class="img"></div>
+</div>
+
 <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer">
 
   <div class="mdl-layout__drawer">
@@ -72,7 +82,9 @@
         <section id="orbits"></section>
       </div>
       <div class="mdl-cell mdl-cell--8-col">
-        <section id="time_series"></section>
+        <section id="time_series">
+          <p id="zoom_controls_help" class="help mdl-cell--8-col">Drag to zoom in, double click to zoom out.</p>
+        </section>
       </div>
     </div>
 
@@ -89,6 +101,103 @@
 {#      background-color: #322e3f;#}
 {#      color: #e3e3e3;#}
     }
+    #plots_wrapper {
+{#      position: relative;#}
+    }
+    #plots_loader {
+      position: absolute;
+      top: 0; left: 0; bottom: 0; right: 0;
+      height: 100%;
+      width: 100%;
+      background-color: #fff;
+      z-index: 1000;
+    }
+
+    #plots_loader .loader-text {
+      width: 200px;
+      height: 30px;
+      position: absolute;
+      top: -240px; left: -32px; bottom: 0; right: 0;
+      margin: auto;
+      text-align: center;
+      font-size: 1.0em;
+      font-style: italic;
+      color: darkgrey;
+    }
+
+    #plots_loader .img {
+      width: 100px;
+      height: 100px;
+      border-radius: 100%;
+      position: absolute;
+      border: 1px solid #6978ff;
+      animation: up 1s;
+      animation-iteration-count: infinite;
+      transition: 2s;
+      border-bottom: none;
+      border-right: none;
+      animation-timing-function: linear;
+      margin-left: -70px;
+      margin-top: -70px;
+      left: 50%;
+      top: 50%;
+    }
+
+    @keyframes up {
+      from {
+        transform: rotate(0deg);
+      }
+      50% {
+        transform: rotate(180deg);
+      }
+      100% {
+        transform: rotate(360deg);
+      }
+    }
+
+    #plots_loader #plots_loader_img2 {
+      width: 90px;
+      height: 90px;
+      left: 50.35%;
+      top: 50.7%;
+      animation-delay: .2s;
+    }
+
+    #plots_loader #plots_loader_img3 {
+      width: 80px;
+      height: 80px;
+      left: 50.70%;
+      top: 51.4%;
+      animation-delay: .4s;
+    }
+
+    #plots_loader #plots_loader_img4 {
+      width: 70px;
+      height: 70px;
+      left: 51.05%;
+      top: 52.1%;
+      animation-delay: .6s;
+    }
+
+    #plots_loader #plots_loader_img5 {
+      width: 60px;
+      height: 60px;
+      left: 51.40%;
+      top: 52.8%;
+      animation-delay: .8s;
+    }
+
+    #time_series .help {
+      position: absolute;
+      text-align: center;
+      font-size: 0.9em;
+      font-style: italic;
+      color: darkgrey;
+      display: none;
+    }
+    #time_series:hover .help {
+      display: block;
+    }
     .axis path, .axis line {
       fill: none;
 {#      stroke: #f4f4f4;#}
@@ -288,7 +397,7 @@ jQuery().ready(function($){
   var isLastTargetEnabled = function(target_slug) {
     var s = 0;
     $(".orbiters_filters .target:not(.locked)").each(function(i,p) {
-      if ($(p).hasClass('active')) s++;
+      if ($(p).hasClass('active') && ! $(p).hasClass('loading')) s++;
     });
     return s < 2;
   };
@@ -304,7 +413,8 @@ jQuery().ready(function($){
     }
     return false;
   });
-  $(".orbiters_filters .target:not(.locked)").click(function(e){
+  $(".orbiters_filters .target:not(.locked)").on("click", function(e){
+    if ($(this).hasClass('loading')) return false;
     var switch_on = ! $(this).hasClass('active');
     var target_slug = $(this).attr('data-target-slug');
     if (switch_on) {
--
libgit2 0.21.2