jekyll-theme-conference 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ })();