Projekt

Obecné

Profil

Stáhnout (22.2 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 areCoordsIdentical = (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="place-intesity"><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="place-intesity">
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

    
299
  if (infoKeys.length === 1) {
300
    // return number of records in one dataset (one dataset in area)
301
    return info[infoKeys[0]].items.length
302
  }
303
  // return number of datasets (agregation of all datasets in area)
304
  return infoKeys.length
305
}
306

    
307
const getPopupDataOnPage = (pageInPopup) => {
308
  const keys = Object.keys(info)
309
  return info[keys[pageInPopup]]
310
}
311

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

    
316
const setPopupDatasetClassName = (datasetName) => {
317
  const popup = $('.leaflet-popup')
318

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

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

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

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

    
338
  let total = 0
339

    
340
  const datasetsInRadius = {}
341
  const eventCoord = {
342
    lng: e.latlng.lng,
343
    lat: e.latlng.lat
344
  }
345

    
346
  Object.keys(data[currentTime]).forEach(key => {
347
    const namedData = data[currentTime][key]
348

    
349
    namedData.items.forEach(element => {
350
      if (e.latlng.distanceTo(new L.LatLng(element.x, element.y)) < radius) {
351
        lat += element.x
352
        lng += element.y
353
        info[i] = { place: element.place, number: element.number, datasetName: key }
354
        total += parseInt(element.number)
355
        i++
356
        datasetsInRadius[key] = true
357
      }
358
    })
359
  })
360

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

    
371
    acc[item.datasetName].items.push(item)
372
    acc[item.datasetName].number += Number(item.number)
373
    return acc
374
  }, {})
375

    
376
  const countDatasets = Object.keys(datasetsInRadius).length
377

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

    
385
    return
386
  }
387

    
388
  if (countDatasets === 1) {
389
    const markersInRadius = getPopupDataOnPage(0)
390
    const popupPagesData = markersInRadius.items
391
    const { place, number } = popupPagesData[currentPageInPopup]
392

    
393
    if (!globalPopup._popup || !areCoordsIdentical(globalPopup.coord, eventCoord)) {
394
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${markersInRadius.datasetName}`)
395
      globalPopup.coord = eventCoord
396
    }
397
    else {
398
      setPopupDatasetClassName(markersInRadius.datasetName)
399
    }
400

    
401
    setGlobalPopupContent(getPopupContent(datasetDictNameDisplayName[markersInRadius.datasetName], place, number, total, 1, popupPagesData.length))
402

    
403
    if (popupPagesData.length === 1) {
404
      disablePopupControls()
405
    }
406
  } else {
407
    const { datasetName, number } = getPopupDataOnPage(currentPageInPopup)
408

    
409
    if (!globalPopup._popup || !areCoordsIdentical(globalPopup.coord, eventCoord)) {
410
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
411
      globalPopup.coord = eventCoord
412
    }
413
    else {
414
      setPopupDatasetClassName(datasetName)
415
    }
416

    
417
    setGlobalPopupContent(genMultipleDatasetsPopUp(number, 1, getCountPagesInPopup(), datasetDictNameDisplayName[datasetName]))
418
  }
419
}
420

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

    
426
  currentPageInPopup = (currentPageInPopup + countPagesInPopup - 1) % countPagesInPopup
427
  setPageContentInPopup(page)
428
}
429

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

    
435
  currentPageInPopup = (currentPageInPopup + 1) % countPagesInPopup
436
  setPageContentInPopup(page)
437
}
438

    
439
function setPageContentInPopup (page) {
440
  const previousPageData = areMultipleDatasetsInRadius() ? getPopupDataOnPage(page) : getPopupDataOnPage(0).items[page]
441
  const currentPageData = areMultipleDatasetsInRadius() ? getPopupDataOnPage(currentPageInPopup) : getPopupDataOnPage(0).items[currentPageInPopup]
442
  const datasetName = $('#dataset-name')
443

    
444
  if (datasetName) {
445
    datasetName.html(datasetDictNameDisplayName[currentPageData.datasetName])
446
  }
447

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

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

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

    
461
  mymap.setView([latitude, longitude], zoom)
462
}
463

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

    
471
  isAnimationRunning = !isAnimationRunning
472

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

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

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

    
498
  updatePopup()
499
}
500

    
501
async function next () {
502
  if (loading) {
503
    return
504
  }
505

    
506
  currentTime = (currentTime + 1) % 24
507
  changeHour(currentTime)
508

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

    
516
  updatePopup()
517
}
518

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

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

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

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

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

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

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

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

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

    
576
const updatePopup = () => {
577
  const { _popup } = mymap
578

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

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

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

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

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

    
612
  loadingY(loaderDelay)
613

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

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

    
630
  marksLayer = L.layerGroup()
631

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

    
648
  marksLayer.setZIndex(-1).addTo(mymap)
649
}
650

    
651
async function preload (time, change, date) {
652
  loadingY()
653

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

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

    
666
  loadingN()
667
}
668

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

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

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

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

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

    
708
  if (heatmapLayer != null) {
709
    mymap.removeLayer(heatmapLayer)
710
  }
711

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

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

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

    
736
function updateAvailableDataSets (available) {
737
  let leastOneOptionEnabled = false
738

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

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

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

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

    
759
  if (day.length === 1) {
760
    day = '0' + day
761
  }
762

    
763
  if (month.length === 1) {
764
    month = '0' + month
765
  }
766

    
767
  return date.getFullYear() + '-' + month + '-' + day
768
}
769

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

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

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

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

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

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

    
819
const loadCheckboxDatasetNameData = () => {
820
  datasetSelected = []
821

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

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

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

    
834
const dragTimeline = () => {
835
  const hourElemWidthPx = 26
836

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

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

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

    
847
      onChangeHour(hour)
848
    }
849
  }
850
}
(2-2/2)