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';
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.
16 * 1. Initialization block
19 export class SimulationController
extends HTMLElement
{
20 /** ===== Initialization block ===== */
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>
32 <button class='slider-button' id='slider-prev'>
33 <svg class='svgIcon slider-icon'>
34 <use href="#arrow_left-24px"></use>
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>
41 <svg id='pause-button' class='svgIcon slider-icon hidden'>
42 <use href="#pause-24px"></use>
45 <button class='slider-button' id='slider-next'>
46 <svg class='svgIcon slider-icon'>
47 <use href="#arrow_right-24px"></use>
50 <button class='slider-button' id='slider-fast-forward'>
51 <svg class='svgIcon slider-icon'>
52 <use href="#fast_forward_black_24dp"></use>
56 <div id='slider-timestamp'>
57 <span id='timestamp'></span>
64 this.frameRate
= NORMAL_RATE
;
65 this.simulationSlider
;
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(() => {
84 }, controllerEvents
.ALL
);
85 controllers
.currentTimestamp
.subscribe(() => {
89 document
.addEventListener('keydown', (e
) => {
90 e
= e
|| window
.event
;
91 if (e
.key
== 'ArrowRight') {
94 if (e
.key
== 'ArrowLeft') {
99 this.querySelector('#slider-play-pause').onpointerdown
= () => {
102 this.querySelector('#slider-prev').onpointerdown
= () => {
106 this.querySelector('#slider-next').onpointerdown
= () => {
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
;
128 this.frameRate
= rate
;
129 togglePrimary
.style
.background
= '#e5e5e5';
138 const sliderContainer
= this.querySelector('.slider-container');
139 sliderContainer
.style
.display
= (simVars
.sortedTimestamps
.length
< 2) ? 'none' : 'block';
143 /** Called to update the UI when the currentFrame has been updated. */
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. */
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
;
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');
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');
179 /** Iterates to next frame while still playing */
182 var endDate
= controllers
.endDate
.getValue();
183 var nextTimestamp
= this.nextFrame();
184 if (nextTimestamp
== endDate
) {
185 window
.setTimeout(() => this.play(), 2*this.frameRate
);
187 window
.setTimeout(() => this.play(), this.frameRate
);
192 /** Moves one frame to the right. */
194 var nextTimestamp
= this.simulationSlider
.nextTimestamp();
196 controllers
.currentTimestamp
.setValue(nextTimestamp
);
197 return nextTimestamp
;
200 /** Moves one frame to the left. */
202 var prevTimestamp
= this.simulationSlider
.prevTimestamp();
204 controllers
.currentTimestamp
.setValue(prevTimestamp
);
205 return prevTimestamp
;
209 window
.customElements
.define('simulation-controller', SimulationController
);