REFACTOR Removes magic numbers from simulationController.
[wrfxweb.git] / fdds / js / components / simulationController.js
blob95d441852c4d96fd26a89f22b6cee728990c4b04
1 import { controllerEvents, controllers } from './Controller.js';
2 import { 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.
19 export class SimulationController extends HTMLElement {
20 /** ===== Initialization block ===== */
21 constructor() {
22 super();
23 this.innerHTML = `
24 <div class='slider-container'>
25 <div id='slider-header'>
26 <div id='slider-play-bar'>
27 <button class='slider-button' id='slider-slow-down'>
28 <svg class='svgIcon slider-icon'>
29 <use href="#fast_rewind_black_24dp"></use>
30 </svg>
31 </button>
32 <button class='slider-button' id='slider-prev'>
33 <svg class='svgIcon slider-icon'>
34 <use href="#arrow_left-24px"></use>
35 </svg>
36 </button>
37 <button class='slider-button' id='slider-play-pause'>
38 <svg id='play-button' class='svgIcon slider-icon'>
39 <use href="#play_arrow-24px"></use>
40 </svg>
41 <svg id='pause-button' class='svgIcon slider-icon hidden'>
42 <use href="#pause-24px"></use>
43 </svg>
44 </button>
45 <button class='slider-button' id='slider-next'>
46 <svg class='svgIcon slider-icon'>
47 <use href="#arrow_right-24px"></use>
48 </svg>
49 </button>
50 <button class='slider-button' id='slider-fast-forward'>
51 <svg class='svgIcon slider-icon'>
52 <use href="#fast_forward_black_24dp"></use>
53 </svg>
54 </button>
55 </div>
56 <div id='slider-timestamp'>
57 <span id='timestamp'></span>
58 </div>
59 </div>
60 </div>
63 this.playing = false;
64 this.frameRate = NORMAL_RATE;
65 this.simulationSlider;
68 connectedCallback() {
69 const container = this.querySelector('.slider-container');
70 const slider = new SimulationSlider();
71 container.appendChild(slider);
72 this.simulationSlider = slider;
74 if (document.body.clientWidth < 769) {
75 const timeStamp = this.querySelector('#slider-timestamp');
76 const playButtons = this.querySelector('#slider-play-bar');
77 timeStamp.parentNode.insertBefore(timeStamp, playButtons);
79 L.DomEvent.disableScrollPropagation(container);
80 L.DomEvent.disableClickPropagation(container);
82 controllers.currentDomain.subscribe(() => {
83 this.resetSlider();
84 }, controllerEvents.ALL);
85 controllers.currentTimestamp.subscribe(() => {
86 this.updateSlider();
87 });
89 document.addEventListener('keydown', (e) => {
90 e = e || window.event;
91 if (e.key == 'ArrowRight') {
92 this.nextFrame();
94 if (e.key == 'ArrowLeft') {
95 this.prevFrame();
97 });
99 this.querySelector('#slider-play-pause').onpointerdown = () => {
100 this.playPause();
102 this.querySelector('#slider-prev').onpointerdown = () => {
103 this.prevFrame();
104 setURL();
106 this.querySelector('#slider-next').onpointerdown = () => {
107 this.nextFrame();
108 setURL();
110 const speedUp = this.querySelector('#slider-fast-forward');
111 const slowDown = this.querySelector('#slider-slow-down');
112 speedUp.onpointerdown = () => {
113 this.toggleRate(FAST_RATE, speedUp, slowDown);
115 slowDown.onpointerdown = () => {
116 this.toggleRate(SLOW_RATE, slowDown, speedUp);
120 toggleRate(rate, togglePrimary, toggleSecondary) {
121 var unPressedColor = '#d6d6d6';
122 togglePrimary.style.background = unPressedColor;
123 toggleSecondary.style.background = unPressedColor;
125 if (this.frameRate == rate) {
126 this.frameRate = NORMAL_RATE;
127 } else {
128 this.frameRate = rate;
129 togglePrimary.style.background = '#e5e5e5';
133 resetSlider() {
134 if (this.playing) {
135 this.playPause();
138 const sliderContainer = this.querySelector('.slider-container');
139 sliderContainer.style.display = (simVars.sortedTimestamps.length < 2) ? 'none' : 'block';
140 this.updateSlider();
143 /** Called to update the UI when the currentFrame has been updated. */
144 updateSlider() {
145 var currentTimestamp = controllers.currentTimestamp.getValue();
146 this.querySelector('#timestamp').innerText = utcToLocal(currentTimestamp);
149 /** Called when play/pause button clicked. Starts animation, disables prev / next buttons
150 * changes play icon to pause icon. */
151 playPause() {
152 const prevButton = this.querySelector('#slider-prev');
153 const nextButton = this.querySelector('#slider-next');
154 const playButton = this.querySelector('#play-button');
155 const pauseButton = this.querySelector('#pause-button');
157 this.playing = !this.playing;
158 if (!this.playing) {
159 playButton.classList.remove('hidden');
160 pauseButton.classList.add('hidden');
162 prevButton.disabled = false;
163 nextButton.disabled = false;
164 prevButton.classList.remove('disabled-button');
165 nextButton.classList.remove('disabled-button');
166 setURL();
167 } else {
168 playButton.classList.add('hidden');
169 pauseButton.classList.remove('hidden');
171 prevButton.disabled = true;
172 nextButton.disabled = true;
173 prevButton.classList.add('disabled-button');
174 nextButton.classList.add('disabled-button');
175 this.play();
179 /** Iterates to next frame while still playing */
180 play() {
181 if (this.playing) {
182 var endDate = controllers.endDate.getValue();
183 var nextTimestamp = this.nextFrame();
184 if (nextTimestamp == endDate) {
185 window.setTimeout(() => this.play(), 2*this.frameRate);
186 } else {
187 window.setTimeout(() => this.play(), this.frameRate);
192 /** Moves one frame to the right. */
193 nextFrame() {
194 var nextTimestamp = this.simulationSlider.nextTimestamp();
196 controllers.currentTimestamp.setValue(nextTimestamp);
197 return nextTimestamp;
200 /** Moves one frame to the left. */
201 prevFrame() {
202 var prevTimestamp = this.simulationSlider.prevTimestamp();
204 controllers.currentTimestamp.setValue(prevTimestamp);
205 return prevTimestamp;
209 window.customElements.define('simulation-controller', SimulationController);