jekyll-theme-conference 3.2.0 → 3.5.0

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