For Lidar profiles, starts the simulation at the latest timestamp
[wrfxweb.git] / fdds / js / components / simulationController.js
blob3a2ce6091cdbac8c638290be02bfc328c6665c91
1 import { controllerEvents, controllers } from './Controller.js';
2 import { ELEMENT_FOCUSED, IS_MOBILE, setURL, utcToLocal } from '../util.js';
3 import { SimulationSlider } from './simulationSlider.js';
4 import { simVars } from '../simVars.js';
6 const FAST_RATE = 150;
7 const SLOW_RATE = 500;
8 const NORMAL_RATE = 330;
10 /** Creates a UI component that includes a play / pause / prev / next buttons to iterate through
11 * the simulation. Also includes a slider bar with a head that indicates relative position in
12 * simulation that can be dragged to a specific location. Bar can also be clicked to seek
13 * to a specific position.
15 * Contents
16 * 1. Initialization block
17 * 2. UI block
18 * 3. FrameNavigation block
20 export class SimulationController extends HTMLElement {
21 /** ===== Initialization block ===== */
22 constructor() {
23 super();
24 this.innerHTML = `
25 <div id='sim-controller' class='slider-container hidden'>
26 <div id='slider-header'>
27 <div id='slider-play-bar'>
28 <button class='slider-button' id='slider-slow-down'>
29 <svg class='svgIcon slider-icon'>
30 <use href="#fast_rewind_black_24dp"></use>
31 </svg>
32 </button>
33 <button class='slider-button' id='slider-prev'>
34 <svg class='svgIcon slider-icon'>
35 <use href="#arrow_left-24px"></use>
36 </svg>
37 </button>
38 <button class='slider-button' id='slider-play-pause'>
39 <svg id='play-button' class='svgIcon slider-icon'>
40 <use href="#play_arrow-24px"></use>
41 </svg>
42 <svg id='pause-button' class='svgIcon slider-icon hidden'>
43 <use href="#pause-24px"></use>
44 </svg>
45 </button>
46 <button class='slider-button' id='slider-next'>
47 <svg class='svgIcon slider-icon'>
48 <use href="#arrow_right-24px"></use>
49 </svg>
50 </button>
51 <button class='slider-button' id='slider-fast-forward'>
52 <svg class='svgIcon slider-icon'>
53 <use href="#fast_forward_black_24dp"></use>
54 </svg>
55 </button>
56 </div>
57 <div id='slider-timestamp'>
58 <span id='timestamp'></span>
59 </div>
60 </div>
61 </div>
64 this.playing = false;
65 this.frameRate = NORMAL_RATE;
66 this.simulationSlider;
69 connectedCallback() {
70 this.initializeContainer();
71 this.responsiveUI();
72 this.createSimulationSlider();
73 this.initializeFrameNavigation();
74 this.initializeFrameRates();
76 controllers.currentDomain.subscribe(() => {
77 this.resetSlider();
78 }, controllerEvents.ALL);
79 controllers.currentTimestamp.subscribe(() => {
80 this.updateDisplayedTimestamp();
81 });
84 initializeContainer() {
85 const container = this.querySelector('.slider-container');
86 L.DomEvent.disableScrollPropagation(container);
87 L.DomEvent.disableClickPropagation(container);
90 createSimulationSlider() {
91 const container = this.querySelector('.slider-container');
93 const slider = new SimulationSlider();
94 container.appendChild(slider);
95 this.simulationSlider = slider;
98 initializeFrameNavigation() {
99 document.addEventListener('keydown', (e) => {
100 e = e || window.event;
101 if (ELEMENT_FOCUSED) {
102 return;
105 if (e.key == 'ArrowRight') {
106 this.nextFrame();
108 if (e.key == 'ArrowLeft') {
109 this.prevFrame();
113 this.querySelector('#slider-play-pause').onpointerdown = () => {
114 this.playPause();
116 this.querySelector('#slider-prev').onpointerdown = () => {
117 this.prevFrame();
118 setURL();
120 this.querySelector('#slider-next').onpointerdown = () => {
121 this.nextFrame();
122 setURL();
126 initializeFrameRates() {
127 const speedUp = this.querySelector('#slider-fast-forward');
128 const slowDown = this.querySelector('#slider-slow-down');
130 speedUp.onpointerdown = () => {
131 this.toggleRate(FAST_RATE, speedUp, slowDown);
133 slowDown.onpointerdown = () => {
134 this.toggleRate(SLOW_RATE, slowDown, speedUp);
138 resetSlider() {
139 const sliderContainer = this.querySelector('#sim-controller');
141 if (this.playing) {
142 this.playPause();
144 if (simVars.sortedTimestamps.length < 2) {
145 sliderContainer.classList.add('hidden');
146 } else {
147 sliderContainer.classList.remove('hidden');
150 this.updateDisplayedTimestamp();
153 /** ===== UI block ===== */
154 responsiveUI() {
155 if (IS_MOBILE) {
156 const timeStamp = this.querySelector('#slider-timestamp');
157 const playButtons = this.querySelector('#slider-play-bar');
158 timeStamp.parentNode.insertBefore(timeStamp, playButtons);
162 updateDisplayedTimestamp() {
163 let currentTimestamp = controllers.currentTimestamp.getValue();
164 this.querySelector('#timestamp').innerText = utcToLocal(currentTimestamp);
167 setPlayingUI() {
168 const prevButton = this.querySelector('#slider-prev');
169 const nextButton = this.querySelector('#slider-next');
170 const playButton = this.querySelector('#play-button');
171 const pauseButton = this.querySelector('#pause-button');
173 playButton.classList.add('hidden');
174 pauseButton.classList.remove('hidden');
175 prevButton.disabled = true;
176 nextButton.disabled = true;
177 prevButton.classList.add('disabled-button');
178 nextButton.classList.add('disabled-button');
181 setPausedUI() {
182 const prevButton = this.querySelector('#slider-prev');
183 const nextButton = this.querySelector('#slider-next');
184 const playButton = this.querySelector('#play-button');
185 const pauseButton = this.querySelector('#pause-button');
187 playButton.classList.remove('hidden');
188 pauseButton.classList.add('hidden');
189 prevButton.disabled = false;
190 nextButton.disabled = false;
191 prevButton.classList.remove('disabled-button');
192 nextButton.classList.remove('disabled-button');
195 /** ===== FrameNavigation block ===== */
196 playPause() {
197 this.playing = !this.playing;
198 if (!this.playing) {
199 this.setPausedUI();
200 setURL();
201 } else {
202 this.setPlayingUI();
203 this.play();
207 /** Iterates to next frame while still playing */
208 play() {
209 if (this.playing) {
210 let endDate = controllers.endDate.getValue();
211 let nextTimestamp = this.nextFrame();
212 if (nextTimestamp == endDate) {
213 window.setTimeout(() => this.play(), 2*this.frameRate);
214 } else {
215 window.setTimeout(() => this.play(), this.frameRate);
220 nextFrame() {
221 let nextTimestamp = this.simulationSlider.nextTimestamp();
223 controllers.currentTimestamp.setValue(nextTimestamp);
224 return nextTimestamp;
227 prevFrame() {
228 let prevTimestamp = this.simulationSlider.prevTimestamp();
230 controllers.currentTimestamp.setValue(prevTimestamp);
231 return prevTimestamp;
234 toggleRate(rate, togglePrimary, toggleSecondary) {
235 let unPressedColor = '#d6d6d6';
236 togglePrimary.style.background = unPressedColor;
237 toggleSecondary.style.background = unPressedColor;
239 if (this.frameRate == rate) {
240 this.frameRate = NORMAL_RATE;
241 } else {
242 this.frameRate = rate;
243 togglePrimary.style.background = '#e5e5e5';
248 window.customElements.define('simulation-controller', SimulationController);