Merge branch 'immediateChanges' of https://github.com/openwfm/wrfxweb into mergeBranches
[wrfxweb.git] / fdds / js / components / simulationSlider.js
blob3126427ac159338a2806d23722dcc24ddb6fcd01
1 import { Slider } from './slider.js';
2 import { utcToLocal, createElement, setURL, IS_MOBILE } from '../util.js';
3 import { controllerEvents, controllers } from './Controller.js';
4 import { simVars } from '../simVars.js';
6 /** Contents
7 * 1. Initialization block
8 * 1.a BoundingDateInitialization block
9 * 2. Getter block
10 * 3. Setter block
11 * 4. Update block
12 * 5. Util block
15 export class SimulationSlider extends Slider {
16 /** ===== Initialization block ===== */
17 constructor() {
18 let width = IS_MOBILE ? 300 : 340;
19 super(width, simVars.sortedTimestamps.length - 1);
20 this.progressWidth = 0;
21 this.progressCheck = 0;
24 connectedCallback() {
25 super.connectedCallback();
26 this.initializeSlider();
28 this.subscibeToCurrentDomain();
29 controllers.currentTimestamp.subscribe(() => this.updateSliderToCurrentTimestamp());
30 this.subsribeToLoadingProgress();
32 this.initializeStartSetter();
33 this.initializeEndSetter();
36 initializeSlider() {
37 const slider = this.querySelector('#slider');
38 const sliderBar = this.querySelector('#slider-bar');
39 const sliderHead = this.querySelector('#slider-head');
41 slider.classList.add('simulation-slider');
42 sliderBar.classList.add('simulation-slider');
43 this.createProgressBar();
45 sliderHead.onpointerdown = (e) => {
46 const finishedCallback = () => setURL();
47 this.dragSliderHead(e, this.frame, this.setTimestamp, finishedCallback);
49 const clickBarCallback = (newTimestamp) => {
50 this.setTimestamp(newTimestamp);
51 setURL();
53 sliderBar.onclick = (e) => {
54 this.clickBar(e, clickBarCallback);
58 subscibeToCurrentDomain() {
59 // assumes that all necessary controllers are set and all I need to do is update my UI.
60 controllers.currentDomain.subscribe(() => {
61 this.nFrames = simVars.sortedTimestamps.length - 1;
62 this.updateStartLocation();
63 this.updateEndLocation();
64 this.updateSliderToCurrentTimestamp();
65 this.updateProgressWidth();
66 }, controllerEvents.ALL);
69 subsribeToLoadingProgress() {
70 controllers.loadingProgress.subscribe(() => {
71 let progress = controllers.loadingProgress.value;
72 if (progress > 0) {
73 if (progress >= this.progressCheck) {
74 this.progressCheck = Math.floor((this.progressCheck + .01)*100)/100;
75 this.setLoadProgress(progress);
77 } else {
78 this.progressCheck = 0;
79 this.setLoadProgress(progress);
81 });
84 createProgressBar() {
85 const slider = this.querySelector('#slider');
86 const sliderStart = createElement('slider-start', 'slider-marker');
87 const sliderEnd = createElement('slider-end', 'slider-marker');
88 const sliderProgress = createElement('slider-progress', 'slider-bar hidden');
89 const sliderMarkerInfo = createElement('slider-marker-info');
91 slider.append(sliderProgress, sliderStart, sliderEnd, sliderMarkerInfo);
94 /** ===== BoundingDateInitialization block ===== */
95 initializeStartSetter() {
96 const sliderStart = this.querySelector('#slider-start');
97 const updateCallback = (timeIndex) => {
98 let endDate = controllers.endDate.getValue();
99 const dateComparator = (newTimestamp) => newTimestamp >= endDate;
100 let boundingIndex = simVars.sortedTimestamps.indexOf(endDate) - 1;
101 this.boundingDateDragUpdate(timeIndex, controllers.startDate, dateComparator, boundingIndex);
103 const finishedCallback = () => {
104 this.boundingDateDragComplete(controllers.startDate);
107 this.setBoundingDateMouseOver(controllers.startDate, sliderStart);
108 this.setBoundingDateMouseOut(sliderStart);
109 this.setBoundingDatePointerDown(controllers.startDate, sliderStart, updateCallback, finishedCallback);
111 controllers.startDate.subscribe(() => {
112 this.updateStartLocation();
113 this.updateProgressWidth();
114 }, controllerEvents.ALL);
117 initializeEndSetter() {
118 const sliderEnd = this.querySelector('#slider-end');
119 const updateCallback = (timeIndex) => {
120 let startDate = controllers.startDate.getValue();
121 const dateComparator = (newTimestamp) => newTimestamp < startDate;
122 let boundingIndex = simVars.sortedTimestamps.indexOf(startDate) + 1;
123 this.boundingDateDragUpdate(timeIndex, controllers.endDate, dateComparator, boundingIndex);
125 const finishedCallback = () => {
126 this.boundingDateDragComplete(controllers.endDate);
129 this.setBoundingDateMouseOver(controllers.endDate, sliderEnd);
130 this.setBoundingDateMouseOut(sliderEnd);
131 this.setBoundingDatePointerDown(controllers.endDate, sliderEnd, updateCallback, finishedCallback);
133 controllers.endDate.subscribe(() => {
134 this.updateEndLocation();
135 this.updateProgressWidth();
136 }, controllerEvents.ALL);
139 setBoundingDateMouseOver(boundingDateController, sliderMarker) {
140 const sliderMarkerInfo = this.querySelector('#slider-marker-info');
141 sliderMarker.onmouseover = () => {
142 let boundingDate = boundingDateController.getValue();
144 this.setSliderMarkerInfo(boundingDate);
145 sliderMarkerInfo.classList.add('hovered');
149 setBoundingDateMouseOut(sliderMarker) {
150 const sliderMarkerInfo = this.querySelector('#slider-marker-info');
151 sliderMarker.onmouseout = () => {
152 sliderMarkerInfo.classList.remove('hovered');
156 setBoundingDatePointerDown(boundingDateController, sliderMarker, updateCallback, finishedCallback) {
157 const sliderMarkerInfo = this.querySelector('#slider-marker-info');
159 sliderMarker.onpointerdown = (e) => {
160 sliderMarkerInfo.classList.add('clicked');
161 let boundingDate = boundingDateController.getValue();
162 let originalFrame = simVars.sortedTimestamps.indexOf(boundingDate);
164 this.setSliderMarkerInfo(boundingDate);
165 this.setLoadProgress(0);
166 this.dragSliderHead(e, originalFrame, updateCallback, finishedCallback);
170 boundingDateDragUpdate(timeIndex, updatingController, dateComparator, boundingIndex) {
171 let newTimestamp = simVars.sortedTimestamps[timeIndex];
172 if (dateComparator(newTimestamp)) {
173 newTimestamp = simVars.sortedTimestamps[boundingIndex];
176 updatingController.setValue(newTimestamp, controllerEvents.SLIDING_VALUE);
177 this.setSliderMarkerInfo(newTimestamp);
180 boundingDateDragComplete(dateController) {
181 const sliderMarkerInfo = this.querySelector('#slider-marker-info');
183 sliderMarkerInfo.classList.remove('clicked');
184 dateController.broadcastEvent(controllerEvents.VALUE_SET);
185 setURL();
188 /** ===== Getter block ===== */
189 getStartLeft() {
190 let startDate = controllers.startDate.getValue();
191 let left = this.getLeftOfDate(startDate);
192 return left - 2;
195 getEndLeft() {
196 let endDate = controllers.endDate.value;
197 let left = this.getLeftOfDate(endDate);
198 return left + 14;
201 getLeftOfDate(date) {
202 let index = simVars.sortedTimestamps.indexOf(date);
203 let left = Math.floor((index / (simVars.sortedTimestamps.length - 1)) * this.sliderWidth * .95);
205 return left;
208 /** ===== Setter block ===== */
209 setTimestamp(timeIndex) {
210 let newTimestamp = simVars.sortedTimestamps[timeIndex];
211 let endDate = controllers.endDate.getValue();
212 let startDate = controllers.startDate.getValue();
214 if (newTimestamp > endDate) {
215 newTimestamp = endDate;
216 } else if (newTimestamp < startDate) {
217 newTimestamp = startDate;
220 controllers.currentTimestamp.setValue(newTimestamp);
223 setLoadProgress(progress) {
224 let progressWidth = progress*this.progressWidth + 2;
226 const progressBar = this.querySelector('#slider-progress');
227 progressBar.classList.remove('hidden');
228 progressBar.style.width = progressWidth + 'px';
229 if (progress == 0) {
230 progressBar.classList.add('hidden');
231 return;
234 let startDate = controllers.startDate.getValue();
235 let startIndex = simVars.sortedTimestamps.indexOf(startDate);
236 let left = Math.floor((startIndex / (simVars.sortedTimestamps.length - 1)) * this.sliderWidth * .95);
238 progressBar.style.left = left + 'px';
241 setSliderMarkerInfo(timeStamp) {
242 const sliderMarkerInfo = this.querySelector('#slider-marker-info');
243 let localTime = utcToLocal(timeStamp);
244 sliderMarkerInfo.innerHTML = localTime;
247 /** ===== Update block ===== */
248 updateProgressWidth() {
249 let startLeft = this.getStartLeft();
250 let endLeft = this.getEndLeft();
251 let totalWidth = endLeft - (startLeft + 4);
252 this.progressWidth = totalWidth;
255 updateStartLocation() {
256 const sliderStart = this.querySelector('#slider-start');
257 let left = this.getStartLeft();
259 sliderStart.style.left = left + 'px';
262 updateSliderToCurrentTimestamp() {
263 let currentTimestamp = controllers.currentTimestamp.getValue();
264 let newFrame = simVars.sortedTimestamps.indexOf(currentTimestamp);
265 this.updateHeadPosition(newFrame);
268 updateEndLocation() {
269 const sliderEnd = this.querySelector('#slider-end');
270 let left = this.getEndLeft();
272 sliderEnd.style.left = left + 'px';
275 /** ===== Util block ===== */
276 nextTimestamp() {
277 let nextFrame = (this.frame + 1) % (this.nFrames + 1);
278 let startDate = controllers.startDate.getValue();
279 let endDate = controllers.endDate.getValue();
281 let nextTimestamp = simVars.sortedTimestamps[nextFrame];
282 if (nextTimestamp > endDate || nextFrame == 0) {
283 nextTimestamp = startDate;
286 return nextTimestamp;
289 prevTimestamp() {
290 let prevFrame = (this.frame - 1) % (this.nFrames + 1);
291 let startDate = controllers.startDate.getValue();
292 let endDate = controllers.endDate.getValue();
294 if (prevFrame < 0) {
295 prevFrame = this.nFrames;
298 let prevTimestamp = simVars.sortedTimestamps[prevFrame];
299 if (prevTimestamp < startDate || prevTimestamp > endDate) {
300 prevTimestamp = endDate;
303 return prevTimestamp;
307 window.customElements.define('simulation-slider', SimulationSlider);