{% extends 'layout.html.jinja2' %} {% set menu_section = 'home' %} {% block title %}Home{% endblock %} {# This is the main template file, along with the layout. #} {# The engine is Jinja2, close to Twig (yet not as good) #} {% block content %} {% macro target_button(target) -%} <div class="target {{ target.slug }} {{ 'locked' if target.locked else 'active' }}" data-target-slug="{{ target.slug }}"> <img width="64px" height="64px" src="{{ static('img/target/'~target.slug~'_256.png') }}" title="{{ target.title }}" alt="{{ target.name }}"> <img width="64px" height="64px" src="{{ static('img/target/empty_128.png') }}" title="No data at specified interval for {{ target.title }}." alt="NO DATA" class="decorator empty"> <img width="64px" height="64px" src="{{ static('img/target/error_128.png') }}" title="There was an error with {{ target.title }}." alt="ERROR" class="decorator error"> <div class="decorator loading" title="Loading data for {{ target.title }}…"> <div class="small-loader-container"> <div class="small-loader-circle-1"> <div class="small-loader-circle-2"> <div class="small-loader-circle-3"> <div class="small-loader-circle-4"> <div class="small-loader-circle-5"> <div class="small-loader-circle-6"> <div class="small-loader-circle-7"> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> {%- endmacro %} {% macro icon(name) %}<i class="fa fa-{{ name }}" aria-hidden="true"></i>{% endmacro %} <div id="plots_loader"> <p class="loader-text">Loading Heliopropa…<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"> <span class="mdl-layout-title" title="Two years maximum.">{{ icon('calendar') }} Time Interval</span> <form id="form_time_interval" action="#"> <div class="mdl-textfield mdl-js-textfield"> <input type="date" id="started_at" name="started_at" title="The date of the beginning of the interval to observe." class="mdl-textfield__input"> <input type="date" id="stopped_at" name="stopped_at" title="The date of the end of the interval to observe. (exclusive)" class="mdl-textfield__input"> </div> <input type="submit" id="apply_new_interval" value="Load new interval" title="Two years maximum. This may take a while." class="mdl-button mdl-js-button mdl-js-ripple-effect"> </form> <br> <hr class="clear"> <span class="mdl-layout-title">{{ icon('eercast') }} Planets</span> <section class="targets-filters"> {% for target in planets %} {{ target_button(target) }} {% endfor %} </section> <br> <hr class="clear"> <span class="mdl-layout-title">{{ icon('rocket') }} Probes & Comets</span> <section class="targets-filters"> {% for target in probes %} {{ target_button(target) }} {% endfor %} {% for target in comets %} {{ target_button(target) }} {% endfor %} </section> <br> <hr class="clear"> {# <span class="mdl-layout-title">{{ icon('database') }} Inputs</span>#} {##} {# <section class="section-drawer">#} {#{% for input in config.inputs %}#} {# <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-input-{{ input.slug }}" title="{{ input.desc }}">#} {# <input type="radio" id="option-input-{{ input.slug }}" class="mdl-radio__button" name="input_slug" value="{{ input.slug }}" {{ 'checked' if input.slug == input_slug }}>#} {# <span class="mdl-radio__label">{{ input.name }}</span>#} {# </label>#} {# <br />#} {#{% endfor %}#} {# <br />#} {# <input type="button" id="apply_new_input" value="Reload for input" title="This may take a while." class="mdl-button mdl-js-button mdl-js-ripple-effect">#} {# </section>#} {##} {# <hr class="clear">#} <span class="mdl-layout-title">{{ icon('flask') }} Parameters</span> <nav id="parameters" class="mdl-navigation"> {% for p in parameters %} <a class="mdl-navigation__link parameter{{ ' active' if p.active }}" data-ts-slug="{{ p.slug }}" href="#">{{ p.name }}</a> {% endfor %} </nav> <div class="mdl-layout-spacer"></div> </div> <main id="plots_wrapper" class="mdl-layout__content"> <div class="mdl-grid"> <div class="mdl-cell mdl-cell--4-col mdl-cell--8-col-tablet mdl-cell--4-col-phone"> <section id="orbits"></section> <div class="plots-actions plots-buttons"> <button id="download" class="mdl-button mdl-button--raised mdl-button--primary" title="Download the raw data for each selected target and parameters."> Download </button> <button id="samp" class="samp mdl-button mdl-button--raised mdl-button--primary mdl-button--disabled" title="Send the data to a connected SAMP hub."> SAMP </button> </div> </div> <div class="mdl-cell mdl-cell--8-col mdl-cell--8-col-tablet mdl-cell--4-col-phone"> <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> </main> </div> {% endblock %} {#### CSS ####################################################################} {% block styles %} <style> html, body { {# background-color: #322e3f;#} {# color: #e3e3e3;#} } .mdl-layout__drawer > .mdl-layout-title { padding-left: 30px; line-height: 42px; } .mdl-layout__drawer > .mdl-layout-title:first-of-type { line-height: 60px; } #plots_wrapper { {# position: relative;#} } #plots_loader { position: fixed; 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: keyframes_rotate 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 keyframes_rotate { 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; } .plots-buttons { text-align: center; margin: 3em auto; } .plots-buttons button { margin: auto 1em; } #time_series svg { cursor: crosshair; } #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;#} shape-rendering: crispEdges; stroke-width: 1px; } svg text { {# fill: #f4f4f4;#} } #time_series svg .brush .selection { fill: #efa02c; fill-opacity: 0.382; } path.line { fill: none; stroke: steelblue; stroke-width: 1px; } circle.cursor-circle { fill: black; stroke: rgba(20, 20, 20, 0.48); } text.cursor-text { {# font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;#} {# font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;#} font-family: "Ubuntu Mono", 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; text-align: right; } text.cursor-text-shadow { stroke: white; stroke-width: 5px; opacity: 0.777 } path.orbit.orbit_section { fill: none; stroke: steelblue; stroke-width: 1.5px; } ellipse.orbit.orbit_ellipse { fill: none; stroke: #a3a3a3; stroke-width: 1px; stroke-dasharray: 5px; } #form_time_interval { padding-left: 30px; } #form_time_interval .mdl-textfield { padding-top: 0; } #started_at, #stopped_at { width: 85%; } .section-drawer { padding-left: 3em; } .targets-filters { padding-left: 17px; } .targets-filters .target { float: left; cursor: pointer; position: relative; } .targets-filters .target:not(.active) { -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -o-filter: grayscale(100%); -ms-filter: grayscale(100%); filter: grayscale(100%); } .targets-filters .target.locked { cursor: not-allowed; } .targets-filters .target.loading { -webkit-animation-name: keyframes_rotate; -webkit-animation-duration: 4000ms; -webkit-animation-iteration-count: infinite; -webkit-animation-timing-function: linear; -moz-animation-name: keyframes_rotate; -moz-animation-duration: 4000ms; -moz-animation-iteration-count: infinite; -moz-animation-timing-function: linear; -ms-animation-name: keyframes_rotate; -ms-animation-duration: 4000ms; -ms-animation-iteration-count: infinite; -ms-animation-timing-function: linear; animation-name: keyframes_rotate; animation-duration: 4000ms; animation-iteration-count: infinite; animation-timing-function: linear; } .targets-filters .target .decorator { position: absolute; top: 0; left: 0; display: none; } .targets-filters .target.empty .decorator.empty { display: block; } .targets-filters .target.error .decorator.error { display: block; } .targets-filters .target .decorator.loading { top: 19px; left: 19px; } .targets-filters .target.loading .decorator.loading { display: block; } #parameters .parameter { outline: 0; } #parameters .parameter.active { background-color: #c8d3e1; } .small-loader-container { width: 27px; margin: 0 auto; background: none; pointer-events: none; } .small-loader-circle-1 { height: 27px; width: 27px; background: rgba(255, 238, 195, 0.72); } .small-loader-circle-2 { height: 22px; width: 22px; background: none; {# background: rgba(56, 53, 194, 0.5);#} } .small-loader-circle-3 { height: 18px; width: 18px; background: rgba(29, 65, 255, 0.9); } .small-loader-circle-4 { height: 13px; width: 13px; background: none; {# background: rgba(98, 109, 237, 0.5);#} } .small-loader-circle-5 { height: 9px; width: 9px; background: rgba(238, 238, 238, 0.8); } .small-loader-circle-6 { height: 4px; width: 4px; background: none; {# background: rgba(0, 0, 0, 0);#} } .small-loader-circle-7 { height: 2px; width: 2px; background: rgb(110, 102, 255); } .small-loader-circle-1, .small-loader-circle-2, .small-loader-circle-3, .small-loader-circle-4, .small-loader-circle-5, .small-loader-circle-6, .small-loader-circle-7 { border-bottom: none; border-radius: 50%; -o-border-radius: 50%; -ms-border-radius: 50%; -webkit-border-radius: 50%; -moz-border-radius: 50%; box-shadow: 0px 0px 0px rgba(0,0,0,0.1); -o-box-shadow: 0px 0px 0px rgba(0,0,0,0.1); -ms-box-shadow: 0px 0px 0px rgba(0,0,0,0.1); -webkit-box-shadow: 0px 0px 0px rgba(0,0,0,0.1); -moz-box-shadow: 0px 0px 0px rgba(0,0,0,0.1); animation-name: small-loader-spin; -o-animation-name: small-loader-spin; -ms-animation-name: small-loader-spin; -webkit-animation-name: small-loader-spin; -moz-animation-name: small-loader-spin; animation-duration: 4600ms; -o-animation-duration: 4600ms; -ms-animation-duration: 4600ms; -webkit-animation-duration: 4600ms; -moz-animation-duration: 4600ms; animation-iteration-count: infinite; -o-animation-iteration-count: infinite; -ms-animation-iteration-count: infinite; -webkit-animation-iteration-count: infinite; -moz-animation-iteration-count: infinite; animation-timing-function: linear; -o-animation-timing-function: linear; -ms-animation-timing-function: linear; -webkit-animation-timing-function: linear; -moz-animation-timing-function: linear; } @keyframes small-loader-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @-o-keyframes small-loader-spin { from { -o-transform: rotate(0deg); } to { -o-transform: rotate(360deg); } } @-ms-keyframes small-loader-spin { from { -ms-transform: rotate(0deg); } to { -ms-transform: rotate(360deg); } } @-webkit-keyframes small-loader-spin { from { -webkit-transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); } } @-moz-keyframes small-loader-spin { from { -moz-transform: rotate(0deg); } to { -moz-transform: rotate(360deg); } } </style> {% endblock %} {#### JAVASCRIPT #############################################################} {% block scripts_footer %} <script type="application/javascript" src="{{ static('js/vendor/d3-custom.js') }}"></script> <script type="application/javascript" src="{{ static('js/vendor/moment.min.js') }}"></script> <script type="application/javascript" src="{{ static('js/vendor/samp.js') }}"></script> <script type="application/javascript" src="{{ static('js/swapp.js') }}"></script> <script type="application/javascript"> var configuration = { time_series_container: '#time_series', orbits_container: '#orbits', api: { 'data_for_interval': "{{ request.url_root }}<target>_{{ input_slug }}_<started_at>_<stopped_at>.csv", 'download': "{{ request.url_root }}<targets>_{{ input_slug }}_<started_at>_<stopped_at>.cdf", 'samp': "{{ request.url_root }}<targets>_{{ input_slug }}_<started_at>_<stopped_at>.cdf" }, sun: { img: '{{ static('img/sun_128.png') }}' }, targets: { {% for target in targets if not target.locked %} {% if not loop.first %},{% endif %} '{{ target.slug }}': { slug: '{{ target.slug }}', name: '{{ target.name }}', active: true, orbit: { a: {{ target.orbit.semimajor or 0 }}, b: {{ target.orbit.semiminor or 0 }} }, img: '{{ static('img/target/'~target.slug~'_128.png') }}' } {% endfor %} }, parameters: [ {% for p in parameters %} { id: "{{ p.slug | escapejs }}", title: "{{ p.name | escapejs }} ({{ p.units | escapejs }})", active: {{ 'true' if p.active else 'false' }}, unit: "{{ p.units | escapejs }}" }{{ ',' if not loop.last }} {% endfor %} ] }; var sw; jQuery().ready(function($){ // Space Weather app itself, handling data downloads and plot draws. sw = new SpaceWeather(configuration); sw.init(); // User Interface (except plots' interactivity, such as mouse hovers) var parameters = $('#parameters'); var isLastParameterEnabled = function(parameter_id) { var s = 0; parameters.find('.parameter[data-ts-slug]').each(function(i,p) { if ($(p).hasClass('active')) s++; }); return s < 2; }; var isLastTargetEnabled = function(target_slug) { var s = 0; $(".targets-filters .target:not(.locked)").each(function(i,p) { if ($(p).hasClass('active') && ! $(p).hasClass('loading')) s++; }); return s < 2; }; parameters.find(".parameter").click(function(e){ var switch_on = ! $(this).hasClass('active'); var parameter_slug = $(this).attr('data-ts-slug'); if (switch_on) { sw.enableParameter(parameter_slug); $(this).addClass('active'); } else if ( ! isLastParameterEnabled(parameter_slug)) { sw.disableParameter(parameter_slug); $(this).removeClass('active'); } return false; }); $(".targets-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) { sw.enableTarget(target_slug); $(this).addClass('active'); } else if ( ! isLastTargetEnabled(target_slug)) { sw.disableTarget(target_slug); $(this).removeClass('active'); } return false; }); $('#apply_new_interval').on("click", function(e){ var started_at = moment($("#started_at").val()); var stopped_at = moment($("#stopped_at").val()); sw.resizeDomain(started_at, stopped_at); return false; }); $('#apply_new_input').on("click", function(e){ var new_input_slug = $("input[name='input_slug']:checked").val(); window.location = "?input_slug="+new_input_slug; return false; }); $('#download').on("click", function(e){ var url = sw.buildDownloadUrl(); console.info("Downloading " + url); $.ajax({ type: 'GET', url: url, processData: false, success: function (data) { window.location = url; }, error: function (xhr) { console.error('Cannot download.', xhr); alert("Our apologies, there was an error while downloading."); } }); return false; }); }); </script> {#### SAMP ###################################################################} <script type="text/javascript"> jQuery().ready(function ($) { // Flag to know when a hub is connected or not var isConnected = false; // Show SAMP button(s) depending on whether the hub is available or not. var onHubAvailability = function (isHubRunning) { $(".samp").each(function (index, element) { if (isHubRunning) { isConnected = true; $(element).removeClass('disabled mdl-button--disabled'); } else { isConnected = false; $(element).addClass('disabled mdl-button--disabled'); } }); }; // Update the document about the presence of any SAMP hub every 10 sec. var connector = new samp.Connector("Sender"); connector.onHubAvailability(onHubAvailability, 10000); window.onunload = function () { connector.unregister(); }; // Clicking on a SAMP button sends the CDF file to the connected hub. $("#samp").click(function (event) { event.stopPropagation(); if ($(this).hasClass('disabled')) { return; } // pitfall : connector.connection is ALWAYS undefined here if ( ! isConnected) { // so we use a bool flag instead alert("We do not detect any connected SAMP hub.\n" + "Wait some more, or try refreshing this page?"); return; } var name = sw.buildSampName(); var url = sw.buildSampUrl(); console.info("Trying to activate SAMP…", name, url); connector.runWithConnection(function (connection) { var msg = new samp.Message("table.load.cdf", { "name": name, "url": url // absolute URL required }); connection.notifyAll([msg]); }, function (error) { var msg = 'Error: ' + error; console.error(msg); alert(msg); // let's yell at the user as well }); }); }); </script> {% endblock %}