Projekt

Obecné

Profil

Stáhnout (22.1 KB) Statistiky
| Větev: | Revize:
1
/* global L */
2
/* global $ */
3

    
4
var mymap
5
var heatmapLayer = null
6
var marksLayer = null
7

    
8
// values for arrow keys
9
const arrowKeyLEFT = 37
10
const arrowKeyRIGHT = 39
11

    
12
var startX = 49.7248
13
var startY = 13.3521
14
var startZoom = 17
15

    
16
var dataSourceRoute
17
let positionsSourceRoute
18

    
19
let currentTime
20

    
21
let currentDate
22

    
23
var timer
24
var isAnimationRunning = false
25
var data = []
26

    
27
//
28
// info = {
29
//  DATASETNAME: {
30
//    items: Array,
31
//    number: Number,
32
//    datasetName: String
33
// }
34
// }
35
//
36
var info = []
37
let currentPageInPopup = 0
38

    
39
// dictionary for names of datasets
40
const datasetDictNameDisplayName = {}
41
var datasetSelected = []
42

    
43
// data only for one day
44
let lockedDay = false
45

    
46
// loading information for async operations
47
let loading = 0
48

    
49
// default loader showup delay
50
const defaultLoaderDelay = 1000
51

    
52
// marks for all datasets
53
const dataSourceMarks = {}
54

    
55
const globalMarkersHolder = {}
56
// all marker from which popup was removed
57
// contains: {key:[L.circle,L.pupup]}
58
// key: x and y, x + '' + y string
59
let globalMarkersChanged = {}
60

    
61
const globalPopup = {
62
  coord: {
63
    lat: 0,
64
    lng: 0
65
  },
66
  _popup: null
67
}
68

    
69
const setGlobalPopupContent = (content) => {
70
  globalPopup._popup.setContent(content)
71
  globalPopup._popup.openOn(mymap)
72
}
73

    
74
const disablePopupControls = () => {
75
  $('#btn-previous-page').prop('disabled', true)
76
  $('#btn-next-page').prop('disabled', true)
77
  $('.popup-controls').hide()
78
}
79

    
80
const areSameCoord = (first, second) => {
81
  return first.lat === second.lat && first.lng === second.lng
82
}
83

    
84
const loadingCallbackNested = (func, delay) => {
85
  setTimeout(() => {
86
    func(loading)
87
    if (loading) {
88
      loadingCallbackNested(func, delay)
89
    }
90
  }, delay)
91
}
92

    
93
const loadingY = (delay = defaultLoaderDelay) => {
94
  loading++
95
  // check after nms if there is something that is loading
96
  loadingCallbackNested(() => loadingCallbackNested((isLoading) => loadingTimeline(isLoading), delay))
97
}
98

    
99
const loadingN = (delay = defaultLoaderDelay) => {
100
  loading--
101
  loadingCallbackNested(() => loadingCallbackNested((isLoading) => loadingTimeline(isLoading)), delay)
102
}
103

    
104
const changeCurrentTime = (time = null) => {
105
  if (time !== null) {
106
    currentTime = time
107
  } else {
108
    currentTime = parseInt($('#dropdown-time input[type="radio"]:checked').val())
109
  }
110
}
111

    
112
const changeCurrentDate = (date = null) => {
113
  const dateInput = $('#date')
114
  currentDate = new Date(date ? date : dateInput.val())
115

    
116
  dateInput.val(currentDateToString())
117
  $('#player-date span').html(`${currentDate.getDate()}. ${currentDate.getMonth() + 1}. ${currentDate.getFullYear()}`)
118

    
119
  data = []
120
}
121

    
122
const currentDayToString = () => {
123
  const day = currentDate.getDate()
124
  return day > 9 ? `${day}` : `0${day}`
125
}
126

    
127
const currentMonthToString = () => {
128
  const month = currentDate.getMonth() + 1
129
  return month > 9 ? `${month}` : `0${month}`
130
}
131

    
132
const currentDateToString = () => `${currentDate.getFullYear()}-${currentMonthToString()}-${currentDayToString()}`
133

    
134
const addDayToCurrentDate = (day) => {
135
  currentDate.setDate(currentDate.getDate() + day)
136
  changeCurrentDate(currentDate)
137
}
138

    
139
const toggleDayLock = () => {
140
  lockedDay = !lockedDay
141
  $('#player-date').toggleClass('lock')
142
}
143

    
144
const fetchByNameDate = async (baseRoute, name, date, currentTime) => {
145
  const headers = new Headers()
146
  const myRequest = new Request(baseRoute + '/' + name + '/' + date + '/' + currentTime, {
147
    method: 'GET',
148
    headers: headers
149
  })
150
  const beforeJson = await fetch(myRequest)
151

    
152
  return beforeJson.json()
153
}
154

    
155
const fetchDataSourceMarks = async (positionRoute, datasetName) => {
156
  const headers = new Headers()
157
  const myRequest = new Request(positionRoute + '/' + datasetName, {
158
    method: 'GET',
159
    headers: headers
160
  })
161
  const beforeJson = await fetch(myRequest)
162

    
163
  return beforeJson.json()
164
}
165

    
166
const getPaginationButtonsInPopup = (currentPage, countPages) => ({
167
  previousButton: '<button id="btn-previous-page" class="circle-button" onclick="setPreviousPageInPopup()"></button>',
168
  pagesList: `<div id="pages">${currentPage} z ${countPages}</div>`,
169
  nextButton: '<button id="btn-next-page" class="circle-button next" onclick="setNextPageInPopup()"></button>'
170
})
171

    
172
const genPopUpControls = (controls) => {
173
  return `<div class="popup-controls">${controls ? controls.reduce((sum, item) => sum + item, '') : ''}</div>`
174
}
175

    
176
const genMultipleDatasetsPopUp = (sum, currentPage, countPages, datasetName) => {
177
  const popupHeader = `<strong id="dataset-name">${datasetName}</strong>`
178
  const popupData = `<div id="number-info"><span id="current-number">${sum}</span></div>`
179
  const { previousButton, nextButton, pagesList } = getPaginationButtonsInPopup(currentPage, countPages)
180

    
181
  return `
182
  ${popupHeader}
183
  ${popupData}
184
  ${genPopUpControls([previousButton, pagesList, nextButton])}
185
  `
186
}
187

    
188
const prepareLayerPopUp = (lat, lng, num, className) => L.popup({
189
  autoPan: false,
190
  className: className
191
}).setLatLng([lat / num, lng / num])
192

    
193
const getPopupContent = (datasetName, placeName, currentCount, sum, currentPage, countPages) => {
194
  const popupHeader = `
195
    <strong>${datasetName}</strong>
196
    <div id="place-name">${placeName}</div>`
197
  const popupData = `
198
    <div id="number-info">
199
      <span id="current-number">${currentCount}</span>
200
      <span id="part-info">${(sum && sum !== Number(currentCount)) ? '/' + sum : ''}</span>
201
    </div>`
202
  const { previousButton, nextButton, pagesList } = getPaginationButtonsInPopup(currentPage, countPages)
203

    
204
  return `
205
  ${popupHeader}
206
  ${popupData}
207
  ${genPopUpControls(countPages > 1 ? [previousButton, pagesList, nextButton] : null)}
208
  `
209
}
210

    
211
const onCheckboxClicked = async (checkbox) => {
212
  if ($(checkbox).prop('checked')) {
213
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute, 0)
214
  } else {
215
    loadCheckboxDatasetNameData()
216

    
217
    data.forEach((item, index) => {
218
      Object.keys(item).forEach((datasetName) => {
219
        if (datasetName === $(checkbox).val()) {
220
          delete data[index][datasetName]
221
        }
222
      })
223

    
224
      drawHeatmap(data[currentTime])
225
    })
226
  }
227

    
228
  updatePopup()
229
  changeUrlParameters()
230
}
231

    
232
const onArrowLeftRightKeysDownRegister = () => {
233
  $(document).keydown(function (e) {
234
    const { which } = e
235
    
236
    if (which === arrowKeyLEFT) {
237
      previous()
238
      e.preventDefault()
239
    } else if (which === arrowKeyRIGHT) {
240
      next()
241
      e.preventDefault()
242
    }
243
  })
244
}
245

    
246
const debounce = (func, delay) => {
247
  let inDebounce
248
  return function () {
249
    const context = this
250
    const args = arguments
251
    clearTimeout(inDebounce)
252
    inDebounce = setTimeout(() => func.apply(context, args), delay)
253
  }
254
}
255

    
256
const onValueChangeRegister = () => {
257
  $('#date').change(function () {
258
    changeCurrentDate($(this).val())
259
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute, 0)
260
    changeUrlParameters()
261
  })
262

    
263
  $('#dropdown-time input[type="radio"]').each(function () {
264
    $(this).change(function () {
265
      changeHour(parseInt($(this).val()))
266
      drawHeatmap(data[currentTime])
267
    })
268
  })
269

    
270
  $('#dropdown-dataset input[type="checkbox"]').each(function () {
271
    $(this).change(
272
      debounce(() => onCheckboxClicked(this), 1000)
273
    )
274
  })
275
}
276

    
277
/**
278
 * Initialize leaflet map on start position which can be default or set based on user action
279
 */
280
// eslint-disable-next-line no-unused-vars
281
function initMap () {
282
  startX = localStorage.getItem('lat') || startX
283
  startY = localStorage.getItem('lng') || startY
284
  startZoom = localStorage.getItem('zoom') || startZoom
285

    
286
  mymap = L.map('heatmap').setView([startX, startY], startZoom)
287

    
288
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
289
    attribution: '',
290
    maxZoom: 19
291
  }).addTo(mymap)
292

    
293
  mymap.on('click', function (e) { showInfo(e) })
294
}
295

    
296
const getCountPagesInPopup = () => {
297
  const infoKeys = Object.keys(info)
298
  if (infoKeys.length === 1) {
299
    // return number of records in one dataset (one dataset in area)
300
    return info[infoKeys[0]].items.length
301
  }
302
  // return number of datasets (agregation of all datasets in area)
303
  return infoKeys.length
304
}
305

    
306
const getElFromObjectInfo = (position) => {
307
  const keys = Object.keys(info)
308
  return info[keys[position]]
309
}
310

    
311
const hasInfoMultipleDatasets = () => {
312
  return Object.keys(info).length > 1
313
}
314

    
315
const setNewPopupDatasetName = (datasetName) => {
316
  const popup = $('.leaflet-popup')
317

    
318
  popup.removeClass(function (index, css) {
319
    return (css.match (/(^|\s)popup-\S+/g) || []).join(' ');
320
  })
321
  popup.addClass(`popup-${datasetName}`)
322
}
323

    
324
function showInfo (e) {
325
  info = []
326
  currentPageInPopup = 0
327

    
328
  // https://wiki.openstreetmap.org/wiki/Zoom_levels
329
  // Todo change to variable - it is used in heatmap init
330
  const stile = 40075016.686 * Math.cos(startX) / Math.pow(2, mymap.getZoom())
331
  const radius = 25 * stile / 256
332

    
333
  let i = 0
334
  let lat = 0
335
  let lng = 0
336

    
337
  let total = 0
338

    
339
  const datasetsInRadius = {}
340
  const eventCoord = {
341
    lng: e.latlng.lng,
342
    lat: e.latlng.lat
343
  }
344
  Object.keys(data[currentTime]).forEach((key) => {
345
    const namedData = data[currentTime][key]
346
    namedData.items.forEach(element => {
347
      if (e.latlng.distanceTo(new L.LatLng(element.x, element.y)) < radius) {
348
        lat += element.x
349
        lng += element.y
350
        info[i] = { place: element.place, number: element.number, datasetName: key }
351
        total += parseInt(element.number)
352
        i++
353
        datasetsInRadius[key] = true
354
      }
355
    })
356
  })
357

    
358
  // Process info for more then one dataset
359
  info = info.reduce((acc, item) => {
360
    if (!acc[item.datasetName]) {
361
      acc[item.datasetName] = {
362
        items: [],
363
        number: 0,
364
        datasetName: item.datasetName
365
      }
366
    }
367

    
368
    acc[item.datasetName].items.push(item)
369
    acc[item.datasetName].number += Number(item.number)
370
    return acc
371
  }, {})
372
  // There is one dataset
373

    
374
  const numDatasets = Object.keys(datasetsInRadius).length
375

    
376
  if (!numDatasets) {
377
    if (mymap._popup) {
378
      $('#part-info').text('')
379
      $('#current-number').html(0)
380
      disablePopupControls()
381
    }
382

    
383
    return
384
  }
385

    
386
  if (numDatasets === 1) {
387
    const infoDict = getElFromObjectInfo(0)
388
    const info_ = infoDict.items
389
    const { place, number } = info_[currentPageInPopup]
390

    
391
    if (!globalPopup._popup || !areSameCoord(globalPopup.coord, eventCoord)) {
392
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${infoDict.datasetName}`)
393
      globalPopup.coord = eventCoord
394
    }
395
    else {
396
      setNewPopupDatasetName(infoDict.datasetName)
397
    }
398

    
399
    setGlobalPopupContent(getPopupContent(datasetDictNameDisplayName[infoDict.datasetName], place, number, total, 1, info_.length))
400

    
401
    if (info_.length === 1) {
402
      disablePopupControls()
403
    }
404
  } else {
405
    const { datasetName, number } = getElFromObjectInfo(currentPageInPopup)
406

    
407
    if (!globalPopup._popup || !areSameCoord(globalPopup.coord, eventCoord)) {
408
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
409
      globalPopup.coord = eventCoord
410
    }
411
    else {
412
      setNewPopupDatasetName(datasetName)
413
    }
414

    
415
    setGlobalPopupContent(genMultipleDatasetsPopUp(number, 1, getCountPagesInPopup(), datasetDictNameDisplayName[datasetName]))
416
  }
417
}
418

    
419
// eslint-disable-next-line no-unused-vars
420
function setPreviousPageInPopup () {
421
  const countPagesInPopup = getCountPagesInPopup()
422
  const page = currentPageInPopup
423

    
424
  currentPageInPopup = (currentPageInPopup + countPagesInPopup - 1) % countPagesInPopup
425
  setPageContentInPopup(page)
426
}
427

    
428
// eslint-disable-next-line no-unused-vars
429
function setNextPageInPopup () {
430
  const countPagesInPopup = getCountPagesInPopup()
431
  const page = currentPageInPopup
432

    
433
  currentPageInPopup = (currentPageInPopup + 1) % countPagesInPopup
434
  setPageContentInPopup(page)
435
}
436

    
437
function setPageContentInPopup (page) {
438
  const previousPageData = hasInfoMultipleDatasets() ? getElFromObjectInfo(page) : getElFromObjectInfo(0).items[page]
439
  const currentPageData = hasInfoMultipleDatasets() ? getElFromObjectInfo(currentPageInPopup) : getElFromObjectInfo(0).items[currentPageInPopup]
440
  const datasetName = $('#dataset-name')
441

    
442
  if (datasetName) {
443
    datasetName.html(datasetDictNameDisplayName[currentPageData.datasetName])
444
  }
445

    
446
  $('#place-name').html(currentPageData.place ? currentPageData.place : currentPageData.datasetName)
447
  $('#current-number').html(currentPageData.number)
448
  $('#pages').html(currentPageInPopup + 1 + ' z ' + getCountPagesInPopup())
449

    
450
  $('.leaflet-popup').removeClass(`popup-${previousPageData.datasetName}`).addClass(`popup-${currentPageData.datasetName}`)
451
}
452

    
453
// eslint-disable-next-line no-unused-vars
454
function setMapView (latitude, longitude, zoom) {
455
  localStorage.setItem('lat', latitude)
456
  localStorage.setItem('lng', longitude)
457
  localStorage.setItem('zoom', zoom)
458

    
459
  mymap.setView([latitude, longitude], zoom)
460
}
461

    
462
/**
463
 * Change animation start from playing to stopped or the other way round
464
 */
465
// eslint-disable-next-line no-unused-vars
466
function changeAnimationState () {
467
  const btnAnimate = $('#animate-btn')
468

    
469
  isAnimationRunning = !isAnimationRunning
470

    
471
  if (isAnimationRunning) {
472
    btnAnimate.removeClass('play').addClass('pause')
473
    timer = setInterval(function () { next() }, 800)
474
  } else {
475
    clearTimeout(timer)
476
    btnAnimate.removeClass('pause').addClass('play')
477
  }
478
}
479

    
480
// eslint-disable-next-line no-unused-vars
481
async function previous () {
482
  if (loading) {
483
    return
484
  }
485

    
486
  currentTime = (currentTime + 23) % 24
487
  changeHour(currentTime)
488
  
489
  if (!lockedDay && currentTime === 23) {
490
    addDayToCurrentDate(-1)
491
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
492
  } else {
493
    drawHeatmap(data[currentTime])
494
  }
495

    
496
  updatePopup()
497
}
498

    
499
async function next () {
500
  if (loading) {
501
    return
502
  }
503

    
504
  currentTime = (currentTime + 1) % 24
505
  changeHour(currentTime)
506

    
507
  if (!lockedDay && currentTime === 0) {
508
    addDayToCurrentDate(1)
509
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
510
  } else {
511
    drawHeatmap(data[currentTime])
512
  }
513

    
514
  updatePopup()
515
}
516

    
517
/**
518
 * Change browser url based on animation step.
519
 */
520
const changeUrlParameters = () => {
521
  window.history.pushState(
522
    '',
523
    document.title,
524
    window.location.origin + window.location.pathname + `?date=${currentDateToString()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type[]=' + current, '')}`
525
  )
526
}
527

    
528
const formatTime = (hours, twoDigitsHours = false) => {
529
  return ((twoDigitsHours && hours < 10) ? '0' : '') + hours + ':00';
530
}
531

    
532
const updateHeaderControls = () => {
533
  $(`#time_${currentTime}`).prop('checked', true)
534
  $('#dropdownMenuButtonTime').html(formatTime(currentTime, true))
535
}
536

    
537
const setTimeline = () => {
538
  $('#player-time > span').text(formatTime(currentTime))
539
  $('#player-time').attr('class', 'time hour-' + currentTime)
540
}
541

    
542
const loadingTimeline = (isLoading) => {
543
  if (isLoading) {
544
    loadingYTimeline()
545
  } else {
546
    loadingNTimeline()
547
  }
548
}
549

    
550
const loadingYTimeline = () => {
551
  $('#player-time > .spinner-border').removeClass('d-none')
552
  $('#player-time > span').text('')
553
}
554

    
555
const loadingNTimeline = () => {
556
  $('#player-time > .spinner-border').addClass('d-none')
557
  setTimeline()
558
}
559

    
560
const onChangeHour = (hour) => {
561
  changeHour(hour)
562
  drawHeatmap(data[currentTime])
563
}
564

    
565
const changeHour = (hour) => {
566
  $('#player-time').removeAttr('style')
567
  changeCurrentTime(hour)
568
  updateHeaderControls()
569
  setTimeline()
570
  changeUrlParameters()
571
  updatePopup()
572
}
573

    
574
const updatePopup = () => {
575
  const { _popup } = mymap
576

    
577
  if (_popup) {
578
    showInfo({
579
      latlng: _popup.getLatLng()
580
    })
581
  }
582
}
583

    
584
/**
585
 * Load and display heatmap layer for current data
586
 * @param {string} opendataRoute route to dataset source
587
 * @param {string} positionsRoute  route to dataset postitions source
588
 */
589
// eslint-disable-next-line no-unused-vars
590
async function loadCurrentTimeHeatmap (opendataRoute, positionsRoute, loaderDelay = defaultLoaderDelay) {
591
  loadCheckboxDatasetNameData()
592

    
593
  dataSourceRoute = opendataRoute
594
  positionsSourceRoute = positionsRoute
595
  const allPromises = []
596
  data[currentTime] = {}
597

    
598
  const dataSelectedHandler = async (datasetName) => {
599
    if (!(datasetName in dataSourceMarks)) {
600
      dataSourceMarks[datasetName] = await fetchDataSourceMarks(positionsRoute, datasetName)
601
    }
602

    
603
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, currentDateToString(), currentTime)
604
    data[currentTime][datasetName] = datasetData
605
  }
606
  datasetSelected.forEach((datasetName) => {
607
    allPromises.push(dataSelectedHandler(datasetName))
608
  })
609

    
610
  loadingY(loaderDelay)
611

    
612
  await Promise.all(allPromises).then(
613
    () => {
614
      loadingN(0)
615
      drawDataSourceMarks(dataSourceMarks)
616
      drawHeatmap(data[currentTime])
617
      preload(currentTime, 1, currentDateToString())
618
      preload(currentTime, -1, currentDateToString())
619
    }
620
  )
621
}
622

    
623
function drawDataSourceMarks (data) {
624
  if (marksLayer != null) {
625
    mymap.removeLayer(marksLayer)
626
  }
627

    
628
  marksLayer = L.layerGroup()
629

    
630
  Object.keys(data).forEach((key_) => {
631
    for (var key in data[key_]) {
632
      const { x, y, name } = data[key_][key]
633
      const pop =
634
          prepareLayerPopUp(x, y, 1, `popup-${key_}`)
635
            .setContent(getPopupContent(datasetDictNameDisplayName[key_], name, 0, 0, 1, 1))
636
      const newCircle =
637
        L.circle([x, y], { radius: 2, fillOpacity: 0.8, color: '#004fb3', fillColor: '#004fb3', bubblingMouseEvents: true })
638
          .bindPopup(pop)
639
      globalMarkersHolder[x + '' + y] = [newCircle, pop] // add new marker to global holders
640
      marksLayer.addLayer(
641
        newCircle
642
      )
643
    }
644
  })
645

    
646
  marksLayer.setZIndex(-1).addTo(mymap)
647
}
648

    
649
async function preload (time, change, date) {
650
  loadingY()
651

    
652
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
653
    if (!data[nTime]) {
654
      data[nTime] = {}
655
    }
656

    
657
    datasetSelected.forEach(async (datasetName) => {
658
      if (!data[nTime][datasetName]) {
659
        data[nTime][datasetName] = await fetchByNameDate(dataSourceRoute, datasetName, date, nTime)
660
      }
661
    })
662
  }
663

    
664
  loadingN()
665
}
666

    
667
function drawHeatmap (dataRaw) {
668
  // Todo still switched
669
  const dataDict = dataRaw
670
  const mergedPoints = []
671
  let max = 0
672

    
673
  if (Object.keys(globalMarkersChanged).length) {
674
    Object.keys(globalMarkersChanged).forEach(function (key) {
675
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
676
    })
677
    globalMarkersChanged = {}
678
  }
679

    
680
  Object.keys(dataDict).forEach((key) => {
681
    const data = dataDict[key]
682
    max = Math.max(max, data.max)
683

    
684
    if (data != null) {
685
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
686
      const points = data.items.map((point) => {
687
        const { x, y, number } = point
688
        const key = x + '' + y
689
        const holder = globalMarkersHolder[key]
690
        if (!globalMarkersChanged[key] && number) {
691
          // There is data for this marker => unbind popup with zero value
692
          holder[0] = holder[0].unbindPopup()
693
          globalMarkersChanged[key] = holder
694
        }
695

    
696
        return [x, y, number]
697
      })
698
      mergedPoints.push(...points)
699
    } else {
700
      if (heatmapLayer != null) {
701
        mymap.removeLayer(heatmapLayer)
702
      }
703
    }
704
  })
705

    
706
  if (heatmapLayer != null) {
707
    mymap.removeLayer(heatmapLayer)
708
  }
709

    
710
  if (mergedPoints.length) {
711
    heatmapLayer = L.heatLayer(mergedPoints, { max: max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
712
  }
713

    
714
  // timto vyresen bug #8191 - TODO: znamena to, ze muzeme smazat volani updatePopup() ve funkcich, kde se nejdriv vola drawHeatmap() a pak updatePopup()?
715
  updatePopup()
716
}
717

    
718
/**
719
 * Checks dataset availibility
720
 * @param {string} route authority for datasets availibility checks
721
 */
722
// eslint-disable-next-line no-unused-vars
723
function checkDataSetsAvailability (route) {
724
  $.ajax({
725
    type: 'POST',
726
    // Todo it might be good idea to change db collections format
727
    url: route + '/' + currentDateToString(),
728
    success: function (result) {
729
      updateAvailableDataSets(result)
730
    }
731
  })
732
}
733

    
734
function updateAvailableDataSets (available) {
735
  let leastOneOptionEnabled = false
736

    
737
  $('#dropdown-dataset .dropdown-item').each(function () {
738
    const input = $(this).find('input')
739
    const inputVal = input[0].value
740

    
741
    if (!(inputVal in available)) {
742
      $(this).addClass('disabled')
743
      $(input).prop('checked', false)
744
    } else {
745
      leastOneOptionEnabled = true
746
      $(this).removeClass('disabled')
747
    }
748
  })
749

    
750
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
751
}
752

    
753
function formatDate (date) {
754
  var day = String(date.getDate())
755
  var month = String(date.getMonth() + 1)
756

    
757
  if (day.length === 1) {
758
    day = '0' + day
759
  }
760

    
761
  if (month.length === 1) {
762
    month = '0' + month
763
  }
764

    
765
  return date.getFullYear() + '-' + month + '-' + day
766
}
767

    
768
// eslint-disable-next-line no-unused-vars
769
function initDatepicker (availableDatesSource) {
770
  var availableDates = ''
771

    
772
  $.ajax({
773
    type: 'GET',
774
    url: availableDatesSource,
775
    success: function (result) {
776
      availableDates = String(result).split(',')
777
    }
778
  }).then(function () {
779
    $('#date').datepicker({
780
      format: 'yyyy-mm-dd',
781
      language: 'cs',
782
      beforeShowDay: function (date) {
783
        if (availableDates.indexOf(formatDate(date)) < 0) {
784
          return { enabled: false, tooltip: 'Žádná data' }
785
        } else {
786
          return { enabled: true }
787
        }
788
      },
789
      autoclose: true
790
    })
791
  })
792
}
793

    
794
function initLocationsMenu () {
795
  const elmLocationsList = $('.locations')
796
  const locationsDisplayClass = 'show'
797

    
798
  if ($(window).width() <= 480) {
799
    elmLocationsList.removeClass(locationsDisplayClass)
800
  } else {
801
    elmLocationsList.addClass(locationsDisplayClass)
802
  }
803
}
804

    
805
function onDocumentReady () {
806
  $('#dropdown-dataset').on('click', function (e) {
807
    e.stopPropagation()
808
  })
809

    
810
  $('#btn-update-heatmap').prop('name', '')
811
  changeCurrentTime()
812
  changeCurrentDate()
813
  onValueChangeRegister()
814
  onArrowLeftRightKeysDownRegister()
815
}
816

    
817
const loadCheckboxDatasetNameData = () => {
818
  datasetSelected = []
819

    
820
  $('#dropdown-dataset .dropdown-item').each(function () {
821
    const input = $(this).find('input')
822
    const inputVal = input[0].value
823

    
824
    if (input[0].checked) {
825
      datasetSelected.push(inputVal)
826
    }
827

    
828
    datasetDictNameDisplayName[inputVal] = $(input).data('dataset-display-name')
829
  })
830
}
831

    
832
const dragTimeline = () => {
833
  const hourElemWidthPx = 26
834

    
835
  const elem = $('#player-time')
836
  const offset = elem.offset().left - elem.parent().offset().left
837

    
838
  if (offset >= 0 && offset <= elem.parent().width()) {
839
    const hour = Math.round(offset / hourElemWidthPx)
840

    
841
    if (hour !== currentTime) {
842
      elem.attr('class', 'time hour-' + hour)
843
      $('#player-time span').html(formatTime(hour))
844

    
845
      onChangeHour(hour)
846
    }
847
  }
848
}
(2-2/2)