REFACTOR Capitalizes enumerated controllerEvents for clearer code
[wrfxweb.git] / fdds / js / components / simulationController.js
bloba7048b34ac1db1960ad1ae1f6dbc5b6234399dcd
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 /**
7 * A Component that builds the animation controller for the simulation. Creates a UI component that
8 * includes a play / pause / prev / next buttons to iterate through the simulation. Also includes a
9 * slider bar with a head that indicates relative position in animation that can be dragged to a
10 * specific location. Bar itself can also be clicked to seek to a specific position.
12 export class SimulationController extends HTMLElement {
13 constructor() {
14 super();
15 this.innerHTML = `
16 <div class='slider-container'>
17 <div id='slider-header'>
18 <div id='slider-play-bar'>
19 <button class='slider-button' id='slider-slow-down'>
20 <svg class='svgIcon slider-icon'>
21 <use href="#fast_rewind_black_24dp"></use>
22 </svg>
23 </button>
24 <button class='slider-button' id='slider-prev'>
25 <svg class='svgIcon slider-icon'>
26 <use href="#arrow_left-24px"></use>
27 </svg>
28 </button>
29 <button class='slider-button' id='slider-play-pause'>
30 <svg id='play-button' class='svgIcon slider-icon'>
31 <use href="#play_arrow-24px"></use>
32 </svg>
33 <svg id='pause-button' class='svgIcon slider-icon hidden'>
34 <use href="#pause-24px"></use>
35 </svg>
36 </button>
37 <button class='slider-button' id='slider-next'>
38 <svg class='svgIcon slider-icon'>
39 <use href="#arrow_right-24px"></use>
40 </svg>
41 </button>
42 <button class='slider-button' id='slider-fast-forward'>
43 <svg class='svgIcon slider-icon'>
44 <use href="#fast_forward_black_24dp"></use>
45 </svg>
46 </button>
47 </div>
48 <div id='slider-timestamp'>
49 <span id='timestamp'></span>
50 </div>
51 </div>
52 </div>
55 this.playing = false;
56 this.fastRate = 150;
57 this.slowRate = 500;
58 this.normalRate = 330;
59 this.frameRate = this.normalRate;
60 this.simulationSlider;
63 /** Called when component is attached to DOM. Sets up functionality for buttons and slider. */
64 connectedCallback() {
65 const container = this.querySelector('.slider-container');
66 const slider = new SimulationSlider();
67 container.appendChild(slider);
68 this.simulationSlider = slider;
70 if (document.body.clientWidth < 769) {
71 const timeStamp = this.querySelector('#slider-timestamp');
72 const playButtons = this.querySelector('#slider-play-bar');
73 timeStamp.parentNode.insertBefore(timeStamp, playButtons);
75 L.DomEvent.disableScrollPropagation(container);
76 L.DomEvent.disableClickPropagation(container);
78 controllers.currentDomain.subscribe(() => {
79 this.resetSlider();
80 }, controllerEvents.ALL);
81 controllers.currentTimestamp.subscribe(() => {
82 this.updateSlider();
83 });
85 document.addEventListener('keydown', (e) => {
86 e = e || window.event;
87 if (e.key == 'ArrowRight') {
88 this.nextFrame();
90 if (e.key == 'ArrowLeft') {
91 this.prevFrame();
93 });
95 this.querySelector('#slider-play-pause').onpointerdown = () => {
96 this.playPause();
98 this.querySelector('#slider-prev').onpointerdown = () => {
99 this.prevFrame();
100 setURL();
102 this.querySelector('#slider-next').onpointerdown = () => {
103 this.nextFrame();
104 setURL();
106 const speedUp = this.querySelector('#slider-fast-forward');
107 const slowDown = this.querySelector('#slider-slow-down');
108 speedUp.onpointerdown = () => {
109 this.toggleRate(this.fastRate, speedUp, slowDown);
111 slowDown.onpointerdown = () => {
112 this.toggleRate(this.slowRate, slowDown, speedUp);
116 toggleRate(rate, togglePrimary, toggleSecondary) {
117 var unPressedColor = '#d6d6d6';
118 togglePrimary.style.background = unPressedColor;
119 toggleSecondary.style.background = unPressedColor;
121 if (this.frameRate == rate) {
122 this.frameRate = this.normalRate;
123 } else {
124 this.frameRate = rate;
125 togglePrimary.style.background = '#e5e5e5';
129 resetSlider() {
130 if (this.playing) {
131 this.playPause();
134 const sliderContainer = this.querySelector('.slider-container');
135 sliderContainer.style.display = (simVars.sortedTimestamps.length < 2) ? 'none' : 'block';
136 this.updateSlider();
139 /** Called to update the UI when the currentFrame has been updated. */
140 updateSlider() {
141 var currentTimestamp = controllers.currentTimestamp.getValue();
142 this.querySelector('#timestamp').innerText = utcToLocal(currentTimestamp);
145 /** Called when play/pause button clicked. Starts animation, disables prev / next buttons
146 * changes play icon to pause icon. */
147 playPause() {
148 const prevButton = this.querySelector('#slider-prev');
149 const nextButton = this.querySelector('#slider-next');
150 const playButton = this.querySelector('#play-button');
151 const pauseButton = this.querySelector('#pause-button');
153 this.playing = !this.playing;
154 if (!this.playing) {
155 playButton.classList.remove('hidden');
156 pauseButton.classList.add('hidden');
158 prevButton.disabled = false;
159 nextButton.disabled = false;
160 prevButton.classList.remove('disabled-button');
161 nextButton.classList.remove('disabled-button');
162 setURL();
163 } else {
164 playButton.classList.add('hidden');
165 pauseButton.classList.remove('hidden');
167 prevButton.disabled = true;
168 nextButton.disabled = true;
169 prevButton.classList.add('disabled-button');
170 nextButton.classList.add('disabled-button');
171 this.play();
175 /** Iterates to next frame while still playing */
176 play() {
177 if (this.playing) {
178 var endDate = controllers.endDate.getValue();
179 var nextTimestamp = this.nextFrame();
180 if (nextTimestamp == endDate) {
181 window.setTimeout(() => this.play(), 2*this.frameRate);
182 } else {
183 window.setTimeout(() => this.play(), this.frameRate);
188 /** Moves one frame to the right. */
189 nextFrame() {
190 var nextTimestamp = this.simulationSlider.nextTimestamp();
192 controllers.currentTimestamp.setValue(nextTimestamp);
193 return nextTimestamp;
196 /** Moves one frame to the left. */
197 prevFrame() {
198 var prevTimestamp = this.simulationSlider.prevTimestamp();
200 controllers.currentTimestamp.setValue(prevTimestamp);
201 return prevTimestamp;
205 window.customElements.define('simulation-controller', SimulationController);