jekyll-theme-conference 3.4.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -21
  3. data/_includes/js/conference.js +26 -16
  4. data/_includes/js/init.js +37 -0
  5. data/_includes/js/{bootstrap.js → lib/bootstrap.js} +0 -0
  6. data/_includes/js/{jquery-3.5.1.min.js → lib/jquery-3.5.1.min.js} +0 -0
  7. data/_includes/js/{leaflet-easybutton.js → lib/leaflet-easybutton.js} +0 -0
  8. data/_includes/js/{leaflet-locatecontrol.js → lib/leaflet-locatecontrol.js} +0 -0
  9. data/_includes/js/{leaflet-providers.js → lib/leaflet-providers.js} +0 -0
  10. data/_includes/js/{leaflet.js → lib/leaflet.js} +0 -0
  11. data/_includes/js/{popper.min.js → lib/popper.min.js} +0 -0
  12. data/_includes/js/{syncscroll.js → lib/syncscroll.js} +0 -0
  13. data/_includes/js/live.js +735 -0
  14. data/_includes/js/map.js +38 -0
  15. data/_includes/js/{conference-modal.js → modal.js} +13 -15
  16. data/_includes/js/{conference-program.js → program.js} +10 -16
  17. data/_includes/partials/checks.html +1 -1
  18. data/_includes/partials/footer.html +1 -1
  19. data/_includes/partials/get_live_timestamps.html +4 -4
  20. data/_includes/partials/get_page_title.html +1 -1
  21. data/_includes/partials/get_talk_timestamp.html +2 -2
  22. data/_includes/partials/header.html +1 -1
  23. data/_includes/partials/modal_live.html +3 -2
  24. data/_includes/partials/navbar.html +1 -1
  25. data/_includes/partials/show_live_button.html +2 -2
  26. data/_layouts/config.html +78 -0
  27. data/_layouts/data.html +29 -3
  28. data/_layouts/home.html +1 -1
  29. data/_layouts/location.html +1 -1
  30. data/_layouts/room.html +1 -1
  31. data/_layouts/stream-overview.html +1 -1
  32. data/assets/js/config.json +3 -0
  33. metadata +17 -14
  34. data/_includes/js/conference-live.js +0 -701
  35. data/_includes/js/conference-map.js +0 -45
@@ -0,0 +1,735 @@
1
+ window.conference.live = (() => {
2
+ let config;
3
+ let lang;
4
+
5
+ let data;
6
+
7
+ let confStart;
8
+ let confEnd;
9
+ let confDur;
10
+
11
+ let stream;
12
+ let streamPause;
13
+ let streamPrepend;
14
+ let streamExtend;
15
+
16
+ let demo;
17
+ let demoStart;
18
+ let demoEnd;
19
+ let demoDur;
20
+ let demoPause;
21
+
22
+ let freezeTime = false;
23
+ let timeFrozen = 0;
24
+ let timeOffset = 0;
25
+
26
+ let liveTimer;
27
+ let streamVideoTimer;
28
+ let streamInfoTimer;
29
+
30
+ let streamModal;
31
+
32
+ const loadData = () => {
33
+ // Load schedule
34
+ const request = new Request(window.conference.config.baseurl + '/assets/js/data.json');
35
+
36
+ fetch(request)
37
+ .then(response =>
38
+ response.json()
39
+ )
40
+ .then(d => {
41
+ data = d;
42
+ })
43
+ .catch((error) => {
44
+ console.log(error);
45
+ });
46
+ };
47
+
48
+ const getData = () => {
49
+ // Return data
50
+ return data;
51
+ };
52
+
53
+ const mod = (n, m) => {
54
+ // Absolute modulo
55
+ return ((n % m) + m) % m;
56
+ };
57
+
58
+ const timeNow = () => {
59
+ // Current timestamp in seconds
60
+ return Math.floor(Date.now() / 1000);
61
+ };
62
+
63
+ const timeCont = () => {
64
+ // Continuous time (respecting previous pauses)
65
+ return timeNow() - timeOffset;
66
+ };
67
+
68
+ const timeCycle = () => {
69
+ // Cyclic timestamp in seconds
70
+ const actTime = timeNow();
71
+ const relTime = mod(actTime, demoDur + 2*demoPause) / (demoDur + 2*demoPause);
72
+ const cycleTime = mod((demoEnd - demoStart) * relTime - timeOffset, (demoEnd - demoStart)) + demoStart;
73
+ return cycleTime;
74
+ };
75
+
76
+ const time = () => {
77
+ // Return app time
78
+ if (freezeTime) {
79
+ return timeFrozen;
80
+ }
81
+ else if (demo) {
82
+ return timeCycle();
83
+ }
84
+ else {
85
+ return timeCont();
86
+ }
87
+ };
88
+
89
+ const pauseTime = () => {
90
+ // Pause app time
91
+ if (!freezeTime) {
92
+ timeFrozen = time();
93
+ freezeTime = true;
94
+
95
+ stopUpdate();
96
+ }
97
+ };
98
+
99
+ const continueTime = () => {
100
+ // Continue app time
101
+ if (freezeTime) {
102
+ freezeTime = false;
103
+ timeOffset += time() - timeFrozen;
104
+ startUpdate();
105
+ }
106
+ };
107
+
108
+ const resetTime = () => {
109
+ // Reset app time
110
+ timeOffset = 0;
111
+ freezeTime = false;
112
+
113
+ startUpdate();
114
+ };
115
+
116
+ const setTime = (newTime, newDay) => {
117
+ // Set and pause app time
118
+ pauseTime();
119
+
120
+ let dayIdx;
121
+ if (!newDay) {
122
+ dayIdx = 0;
123
+ }
124
+ else if (Number.isInteger(newDay)) {
125
+ dayIdx = newDay-1;
126
+ }
127
+ else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(newDay)) {
128
+ dayIdx = data.days.find(o => o.name === newDay);
129
+ }
130
+ else {
131
+ dayIdx = data.days.find(o => o.name === newDay);
132
+ }
133
+ const newDate = data.days[dayIdx].date;
134
+
135
+ let d = new Date(newDate);
136
+ newTime = newTime.split(':');
137
+ d.setHours(newTime[0], newTime[1]);
138
+
139
+ timeFrozen = Math.floor(d.getTime() / 1000);
140
+
141
+ update();
142
+ };
143
+
144
+ const getTime = () => {
145
+ // Return app time as string
146
+ const tConvert = time();
147
+
148
+ const d = new Date(tConvert * 1000);
149
+ const dStr = d.toISOString().slice(0,10);
150
+ const h = d.getHours();
151
+ const m = d.getMinutes();
152
+
153
+ return dStr +" "+ h +":"+ (m < 10 ? "0" : "") + m;
154
+ };
155
+
156
+ const timeUnit = () => {
157
+ // App time refresh rate
158
+ if (demo) {
159
+ return 0.1;
160
+ }
161
+ else {
162
+ return 60;
163
+ }
164
+ };
165
+
166
+ const delayStart = (startTime) => {
167
+ // Seconds until given startTime occurs
168
+ const tNow = time();
169
+ const tUnit = timeUnit();
170
+
171
+ if (demo) {
172
+ // Convert virtual duration to real duration
173
+ return mod(startTime - tNow, demoEnd - demoStart) / (demoEnd - demoStart) * (demoDur + 2*demoPause);
174
+ }
175
+ else {
176
+ if (startTime > tNow) {
177
+ return startTime - tNow;
178
+ }
179
+ else {
180
+ // Start on the unit
181
+ return (tUnit - (tNow % tUnit));
182
+ }
183
+ }
184
+ };
185
+
186
+ const model = {
187
+ set demo(value) {
188
+ demo = value;
189
+ resetTime();
190
+ },
191
+ get demo() {
192
+ return demo;
193
+ }
194
+ };
195
+
196
+ const updateLive = () => {
197
+ // Update status all live elements in DOM
198
+ const tNow = time();
199
+ const liveShow = document.getElementsByClassName('live-show');
200
+ const liveHide = document.getElementsByClassName('live-hide');
201
+ const liveTime = document.getElementsByClassName('live-time');
202
+ const livePast = document.getElementsByClassName('live-past');
203
+
204
+ // Show elements for a given period
205
+ for (let i = 0; i < liveShow.length; i++) {
206
+ const tStarts = liveShow[i].dataset.start.split(',');
207
+ const tEnds = liveShow[i].dataset.end.split(',');
208
+
209
+ for (let k = 0; k < tStarts.length; k++) {
210
+ if (tNow >= tStarts[k] && tNow < tEnds[k]) {
211
+ // Show when active
212
+ liveShow[i].classList.remove('d-none');
213
+ break;
214
+ }
215
+ else if (!liveShow[i].classList.contains('d-none')) {
216
+ // Hide otherwise
217
+ liveShow[i].classList.add('d-none');
218
+ }
219
+ }
220
+ }
221
+
222
+ // Hide elements for a given period
223
+ for (let i = 0; i < liveHide.length; i++) {
224
+ const tStarts = liveHide[i].dataset.start.split(',');
225
+ const tEnds = liveHide[i].dataset.end.split(',');
226
+
227
+ for (let k = 0; k < tStarts.length; k++) {
228
+ if (tNow >= tStarts[k] && tNow < tEnds[k]) {
229
+ // Hide when active
230
+ if (!liveHide[i].classList.contains('d-none')) {
231
+ liveHide[i].classList.add('d-none');
232
+ break;
233
+ }
234
+ }
235
+ else {
236
+ // Show otherwise
237
+ liveHide[i].classList.remove('d-none');
238
+ }
239
+ }
240
+ }
241
+
242
+ // Update duration string for given elements
243
+ for (let i = 0; i < liveTime.length; i++) {
244
+ const t = liveTime[i].dataset.time;
245
+ if (typeof t == "undefined") {
246
+ break;
247
+ }
248
+ let tRel = tNow - t;
249
+
250
+ let tStr;
251
+ if (tRel >= -60 && tRel < 0) {
252
+ tStr = lang.time.soon;
253
+ }
254
+ else if (tRel >= 0 && tRel < 60) {
255
+ tStr = lang.time.now;
256
+ }
257
+ else {
258
+ if (tRel < 0) {
259
+ tStr = lang.time.in;
260
+ }
261
+ else {
262
+ tStr = lang.time.since;
263
+ }
264
+ tRel = Math.abs(tRel);
265
+
266
+ let dWeeks = Math.floor(tRel / (7*24*60*60));
267
+ let dDays = Math.floor(tRel / (24*60*60));
268
+ let dHours = Math.floor(tRel / (60*60));
269
+ let dMins = Math.floor(tRel / (60));
270
+ if (dWeeks > 4) {
271
+ break;
272
+ }
273
+ else if (dWeeks > 1) {
274
+ tStr += dWeeks +' '+ lang.time.weeks;
275
+ }
276
+ else if (dWeeks == 1) {
277
+ tStr += '1 '+ lang.time.week;
278
+ }
279
+ else if (dDays > 1) {
280
+ tStr += dDays +' '+ lang.time.days;
281
+ }
282
+ else if (dDays == 1) {
283
+ tStr += '1 '+ lang.time.day;
284
+ }
285
+ else if (dHours > 1) {
286
+ tStr += dHours +' '+ lang.time.hours;
287
+ }
288
+ else if (dHours == 1) {
289
+ tStr += '1 '+ lang.time.hour;
290
+ }
291
+ else if (dMins > 1) {
292
+ tStr += dMins +' '+ lang.time.minutes;
293
+ }
294
+ else {
295
+ tStr += '1 '+ lang.time.minute;
296
+ }
297
+ }
298
+
299
+ liveTime[i].innerHTML = tStr;
300
+ }
301
+
302
+ // Disable elements for a given period
303
+ for (let i = 0; i < livePast.length; i++) {
304
+ const t = livePast[i].dataset.time;
305
+ if (typeof t == "undefined") {
306
+ break;
307
+ }
308
+ let tRel = tNow - t;
309
+
310
+ if (tRel < 0) {
311
+ if (livePast[i].nodeName == 'A' || livePast[i].nodeName == 'BUTTON') {
312
+ // Disable when in past
313
+ if (!livePast[i].classList.contains('disabled')) {
314
+ livePast[i].classList.add('disabled');
315
+ }
316
+ }
317
+ else {
318
+ // Grey out when in past
319
+ if (!livePast[i].classList.contains('text-secondary')) {
320
+ livePast[i].classList.add('text-secondary');
321
+ }
322
+ }
323
+ }
324
+ else {
325
+ // Show normal otherwise
326
+ livePast[i].classList.remove('disabled');
327
+ livePast[i].classList.remove('text-secondary');
328
+ }
329
+ }
330
+
331
+ // Cancel timer after program is over
332
+ if (tNow > confEnd && !demo) {
333
+ stopUpdateLive();
334
+ }
335
+ };
336
+
337
+ const startUpdateLive = () => {
338
+ // Start update timer to update live elements in DOM
339
+ stopUpdateLive();
340
+ updateLive();
341
+
342
+ if (demo) {
343
+ // Immediate start required since delayStart would wait for next wrap around
344
+ liveTimer = setInterval(updateLive, timeUnit() * 1000);
345
+ }
346
+ else {
347
+ setTimeout(() => {
348
+ liveTimer = setInterval(updateLive, timeUnit() * 1000);
349
+ updateLive();
350
+ }, delayStart(confStart) * 1000);
351
+ }
352
+ };
353
+
354
+ const stopUpdateLive = () => {
355
+ // stopUpdate update timer to update live elements in DOM
356
+ if (typeof liveTimer !== "undefined") {
357
+ clearInterval(liveTimer);
358
+ }
359
+ };
360
+
361
+ const getRoom = (roomName) => {
362
+ // Return room object for given room name
363
+ if (roomName in data.rooms) {
364
+ return data.rooms[roomName];
365
+ }
366
+ else {
367
+ return data.rooms[Object.keys(data.rooms)[0]];
368
+ }
369
+ };
370
+
371
+ const getTalks = (roomName) => {
372
+ if (roomName in data.talks) {
373
+ return data.talks[roomName].map((talk) => {
374
+ // For talks with live links, add some grace period to the end
375
+ // time in order to prevent that the next talk is announced
376
+ // immediately
377
+ const end = talk.live_links && talk.live_links.length > 0 ?
378
+ talk.end + streamExtend * 60 : talk.end;
379
+ return { ...talk, end };
380
+ });
381
+ }
382
+ else {
383
+ return false;
384
+ }
385
+ };
386
+
387
+ const getNextTalk = (roomName) => {
388
+ // Get talk object for next talk in given room
389
+ const timeNow = time();
390
+ const talksHere = getTalks(roomName);
391
+
392
+ if (talksHere) {
393
+ if (timeNow < talksHere[talksHere.length-1].end) {
394
+ for (let i = 0; i < talksHere.length; i++) {
395
+ if (timeNow < talksHere[i].end) {
396
+ return talksHere[i];
397
+ }
398
+ }
399
+ }
400
+ }
401
+ return false;
402
+ };
403
+
404
+ const getNextPause = (roomName) => {
405
+ // Get time object for next pause in given room
406
+ const timeNow = time();
407
+ const talksHere = getTalks(roomName);
408
+
409
+ if (talksHere) {
410
+ if (timeNow < talksHere[talksHere.length-1].end) {
411
+ for (let i = 1; i < talksHere.length; i++) {
412
+ if (timeNow < talksHere[i].start && streamPause*60 <= talksHere[i].start - talksHere[i-1].end) {
413
+ return {
414
+ 'start': talksHere[i-1].end,
415
+ 'end': talksHere[i].start,
416
+ };
417
+ }
418
+ }
419
+ }
420
+ }
421
+ return false;
422
+ };
423
+
424
+ const setStreamIframeContent = (content) => {
425
+ // Set stream modal iframe to show given text
426
+ streamModal.find('iframe').attr('src', '');
427
+ streamModal.find('iframe').addClass('d-none');
428
+ streamModal.find('#stream-placeholder > div').text(content);
429
+ streamModal.find('#stream-placeholder').addClass('d-flex');
430
+ };
431
+
432
+ const setStreamIframeSrc = (href) => {
433
+ // Set stream modal iframe to show given URL
434
+ streamModal.find('iframe').attr('src', href);
435
+ streamModal.find('#stream-placeholder').addClass('d-none').removeClass('d-flex');
436
+ streamModal.find('iframe').removeClass('d-none');
437
+ };
438
+
439
+ const setStreamVideo = (roomName) => {
440
+ // Update stream modal iframe:
441
+ // Show stream with start/pause/end message (for given room) and keep updated
442
+ const timeNow = time();
443
+
444
+ const talksHere = getTalks(roomName);
445
+ let roomStart, roomEnd;
446
+ if (talksHere) {
447
+ roomStart = talksHere[0].start;
448
+ roomEnd = talksHere[talksHere.length-1].end;
449
+ }
450
+ else {
451
+ // If no program for given room, take overall first and last talk
452
+ roomStart = 0;
453
+ roomEnd = 0;
454
+ for (let roomNameTalk in data.talks) {
455
+ talksHere = getTalks(roomNameTalk);
456
+ const crntRoomStart = talksHere[0].start;
457
+ const crntRoomEnd = talksHere[talksHere.length-1].end;
458
+
459
+ if (roomStart == 0 || roomStart > crntRoomStart) {
460
+ roomStart = crntRoomStart;
461
+ }
462
+ if (roomEnd == 0 || roomEnd < crntRoomEnd) {
463
+ roomEnd = crntRoomEnd;
464
+ }
465
+ }
466
+ }
467
+
468
+ if (typeof streamVideoTimer !== "undefined") {
469
+ clearInterval(streamVideoTimer);
470
+ }
471
+
472
+ // Conference not yet started
473
+ if (timeNow <= roomStart - streamPrepend*60) {
474
+ setStreamIframeContent(lang.pre_stream);
475
+
476
+ if (!freezeTime) {
477
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomStart - streamPrepend*60) * 1000, roomName);
478
+ }
479
+ }
480
+
481
+ // Conference is over
482
+ else if (timeNow >= roomEnd + streamExtend*60) {
483
+ setStreamIframeContent(lang.post_stream);
484
+
485
+ if (!freezeTime && demo) {
486
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomEnd - streamPrepend*60) * 1000, roomName);
487
+ }
488
+ }
489
+
490
+ // Conference ongoing
491
+ else {
492
+ const pauseNext = getNextPause(roomName);
493
+
494
+ // Currently stream is paused
495
+ if (pauseNext && timeNow >= pauseNext.start + streamExtend*60 && timeNow <= pauseNext.end - streamPrepend*60) {
496
+ setStreamIframeContent(lang.pause_stream);
497
+
498
+ if (!freezeTime) {
499
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(pauseNext.end - streamPrepend*60) * 1000, roomName);
500
+ }
501
+ }
502
+ // Currently a talk is active
503
+ else {
504
+ const room = getRoom(roomName);
505
+ setStreamIframeSrc(room.href);
506
+
507
+ if (!freezeTime) {
508
+ if (pauseNext) {
509
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(pauseNext.start + streamExtend*60) * 1000, roomName);
510
+ }
511
+ else {
512
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomEnd + streamExtend*60) * 1000, roomName);
513
+ }
514
+ }
515
+ }
516
+ }
517
+ };
518
+
519
+ const setStreamInfo = (roomName) => {
520
+ // Update stream modal info bar:
521
+ // Show next talk and speaker (for given room) and keep updated
522
+ const timeNow = time();
523
+ const talkNext = getNextTalk(roomName);
524
+
525
+ if (typeof streamInfoTimer !== "undefined") {
526
+ clearInterval(streamInfoTimer);
527
+ }
528
+
529
+ if (talkNext && timeNow >= talkNext.start - streamPause*60) {
530
+ document.getElementById('stream-info').dataset.time = talkNext.start;
531
+ document.getElementById('stream-info-time').dataset.time = talkNext.start;
532
+
533
+ streamModal.find('#stream-info-color').removeClass((index, className) => {
534
+ return (className.match(/(^|\s)border-soft-\S+/g) || []).join(' ');
535
+ });
536
+ streamModal.find('#stream-info-color').addClass('border-soft-' + talkNext.color);
537
+
538
+ streamModal.find('#stream-info-talk').text(talkNext.name).attr('href', talkNext.href);
539
+
540
+ let speakerStr = '';
541
+ for (let i = 0; i < talkNext.speakers.length; i++) {
542
+ let speaker = data.speakers[talkNext.speakers[i]];
543
+ if (speaker.href == '') {
544
+ speakerStr += speaker.name +', '
545
+ }
546
+ else {
547
+ speakerStr += '<a class="text-reset" href="'+ speaker.href +'">'+ speaker.name +'</a>, ';
548
+ }
549
+ }
550
+ speakerStr = speakerStr.slice(0, -2);
551
+ streamModal.find('#stream-info-speakers').html(speakerStr);
552
+
553
+ if (talkNext.live_links) {
554
+ let linksStr = '';
555
+ for (let i = 0; i < talkNext.live_links.length; i++) {
556
+ const link = talkNext.live_links[i];
557
+
558
+ // Skip empty links
559
+ if ((link.name == '' && !link.icon) || link.href == '') {
560
+ continue;
561
+ }
562
+
563
+ linksStr += '<a href="' + link.href + '" class="btn btn-light m-1 live-past" data-time="'+ talkNext.start +'">';
564
+ if (link.icon) {
565
+ linksStr += '<i class="fas fa-' + link.icon + '"></i>&nbsp;';
566
+ }
567
+ linksStr += link.name + '</a>';
568
+ }
569
+ streamModal.find('#stream-info-links').html(linksStr).removeClass('d-none');
570
+ }
571
+ else {
572
+ streamModal.find('#stream-info-links').addClass('d-none');
573
+ }
574
+
575
+ streamModal.find('#stream-info').removeClass('d-none');
576
+
577
+ updateLive();
578
+ if (!freezeTime) {
579
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNext.end) * 1000, roomName);
580
+ }
581
+ }
582
+ else {
583
+ streamModal.find('#stream-info').addClass('d-none');
584
+
585
+ if (!freezeTime) {
586
+ if (talkNext) {
587
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNext.start - streamPause*60) * 1000, roomName);
588
+ }
589
+ else if (demo) {
590
+ let talksHere = getTalks(roomName);
591
+ if (talksHere) {
592
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talksHere[0].start - streamPrepend*60) * 1000, roomName);
593
+ }
594
+ }
595
+ }
596
+ }
597
+ };
598
+
599
+ const setStream = (roomName) => {
600
+ // Update stream modal (iframe and info bar) for given room
601
+ streamModal.find('.modal-footer .btn').removeClass('active');
602
+ streamModal.find('#stream-select').val(0);
603
+
604
+ // Recover room name in case of empty default
605
+ const room = getRoom(roomName);
606
+ roomName = room.name;
607
+
608
+ setStreamVideo(roomName);
609
+ setStreamInfo(roomName);
610
+
611
+ streamModal.find('#stream-button' + room.id).addClass('active');
612
+ streamModal.find('#stream-select').val(room.id);
613
+ };
614
+
615
+ const updateStream = () => {
616
+ // Update stream modal for currently active room button
617
+ if (streamModal.hasClass('show')) {
618
+ let activeButton = streamModal.find('.modal-footer .btn.active');
619
+ let roomName = activeButton.data('room');
620
+
621
+ if (typeof roomName !== "undefined") {
622
+ setStream(roomName);
623
+ }
624
+ }
625
+ };
626
+
627
+ const stopUpdateStream = () => {
628
+ // Stop stream modal update timer
629
+ if (typeof streamVideoTimer !== "undefined") {
630
+ clearInterval(streamVideoTimer);
631
+ }
632
+ if (typeof streamInfoTimer !== "undefined") {
633
+ clearInterval(streamInfoTimer);
634
+ }
635
+ };
636
+
637
+ const hideModal = () => {
638
+ // Close stream modal
639
+ streamModal.find('iframe').attr('src', '');
640
+ streamModal.find('.modal-footer .btn').removeClass('active');
641
+ streamModal.find('#stream-select').selectedIndex = -1;
642
+ };
643
+
644
+ const setupStream = () => {
645
+ // Setup events when modal opens/closes
646
+ streamModal = $('#stream-modal');
647
+
648
+ // configure modal opening buttons
649
+ streamModal.on('show.bs.modal', (event) => {
650
+ let button = $(event.relatedTarget);
651
+ let roomName = button.data('room');
652
+ setStream(roomName);
653
+ });
654
+ streamModal.on('hide.bs.modal', () => {
655
+ hideModal();
656
+ });
657
+
658
+ // configure room selection buttons in modal
659
+ streamModal.find('.modal-footer .btn').on('click', function (event) {
660
+ event.preventDefault();
661
+
662
+ let roomName = $(this).data('room');
663
+ setStream(roomName);
664
+ });
665
+
666
+ // configure room selection menu in modal
667
+ streamModal.find('#stream-select').on('change', function (event) {
668
+ event.preventDefault();
669
+
670
+ let roomName = $(this).children('option:selected').text();
671
+ setStream(roomName);
672
+ });
673
+ };
674
+
675
+ const init = (c, l) => {
676
+ config = c;
677
+ lang = l;
678
+
679
+ confStart = config.time.start;
680
+ confEnd = config.time.end;
681
+ confDur = confEnd - confStart;
682
+
683
+ demo = config.demo.enable;
684
+ demoDur = config.demo.duration;
685
+ demoPause = config.demo.pause;
686
+ demoStart = confStart - confDur/demoDur*demoPause;
687
+ demoEnd = confEnd + confDur/demoDur*demoPause;
688
+
689
+ stream = config.streaming.enable;
690
+ streamPause = config.streaming.pause;
691
+ streamPrepend = config.streaming.prepend;
692
+ streamExtend = config.streaming.extend;
693
+
694
+ loadData();
695
+ startUpdateLive();
696
+ if (stream) {
697
+ setupStream();
698
+ }
699
+ };
700
+
701
+ const update = () => {
702
+ updateLive();
703
+ if (stream) {
704
+ updateStream();
705
+ }
706
+ };
707
+
708
+ const startUpdate = () => {
709
+ startUpdateLive();
710
+ if (stream) {
711
+ updateStream();
712
+ }
713
+ };
714
+
715
+ const stopUpdate = () => {
716
+ stopUpdateLive();
717
+ if (stream) {
718
+ stopUpdateStream();
719
+ }
720
+ };
721
+
722
+ return {
723
+ init: init,
724
+ getData: getData,
725
+
726
+ pauseTime: pauseTime,
727
+ continueTime: continueTime,
728
+ resetTime: resetTime,
729
+ setTime: setTime,
730
+ getTime: getTime,
731
+
732
+ demo: model.demo
733
+ };
734
+
735
+ })();