jekyll-theme-conference 2.5.3 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -50
  3. data/_includes/js/conference-live.js +193 -104
  4. data/_includes/js/conference-modal.js +32 -15
  5. data/_includes/js/conference-program.js +48 -0
  6. data/_includes/js/conference.js +11 -11
  7. data/_includes/js/jquery-3.5.1.min.js +2 -0
  8. data/_includes/partials/checks.html +66 -31
  9. data/_includes/partials/footer.html +4 -21
  10. data/_includes/partials/get_day_hash.html +20 -0
  11. data/_includes/partials/get_day_time.html +19 -0
  12. data/_includes/partials/get_link.html +56 -64
  13. data/_includes/partials/get_link_types.html +14 -14
  14. data/_includes/partials/get_live_timestamps.html +49 -0
  15. data/_includes/partials/get_main_category.html +9 -9
  16. data/_includes/partials/get_talk_time.html +7 -7
  17. data/_includes/partials/get_talk_timestamp.html +4 -0
  18. data/_includes/partials/get_time_pronoun.html +6 -0
  19. data/_includes/partials/header.html +15 -12
  20. data/_includes/partials/info_bar.html +22 -22
  21. data/_includes/partials/list_categories.html +2 -2
  22. data/_includes/partials/list_speakers.html +14 -18
  23. data/_includes/partials/list_sub_categories.html +7 -7
  24. data/_includes/partials/modal_link.html +21 -0
  25. data/_includes/partials/{live-modal.html → modal_live.html} +7 -10
  26. data/_includes/partials/navbar.html +32 -32
  27. data/_includes/partials/navbar_rooms.html +21 -20
  28. data/_includes/partials/{live_button.html → show_live_button.html} +6 -8
  29. data/_includes/partials/show_room.html +5 -5
  30. data/_includes/partials/show_talk.html +3 -3
  31. data/_includes/partials/show_talk_duration.html +1 -1
  32. data/_includes/partials/show_talk_time.html +16 -2
  33. data/_layouts/data.html +91 -0
  34. data/_layouts/home.html +17 -18
  35. data/_layouts/program.html +179 -148
  36. data/_layouts/room.html +42 -38
  37. data/_layouts/speaker.html +41 -39
  38. data/_layouts/talk-overview.html +83 -53
  39. data/_layouts/talk.html +38 -39
  40. data/_sass/conference.scss +43 -17
  41. data/assets/icons/live.svg +33 -57
  42. data/assets/js/data.json +3 -0
  43. data/assets/js/main.js +0 -6
  44. metadata +14 -9
  45. data/_includes/js/conference-data.js +0 -87
  46. data/_includes/js/jquery-3.2.1.slim.min.js +0 -4
  47. data/_includes/partials/get_conf_time.html +0 -54
  48. data/_includes/partials/get_timestamp.html +0 -4
  49. data/_layouts/delete_hidden.html +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 582dbe31f3df063b308b3f7b79002541cdde192973decc215a2c5d23c69cf4d1
4
- data.tar.gz: bd427c637147fc3fc2e8c10cc89d8cd95fbb7bab18479837755b098d5ef45815
3
+ metadata.gz: 59b89379465383973e17366c49208b0ef62eec5831c4839ceaf6fbea786b202b
4
+ data.tar.gz: a76584224d9800eaa13164bc3c4a53899e112e997b19d5a4dd552d31ebf3d14e
5
5
  SHA512:
6
- metadata.gz: 38790049e46cd6beed99be5367ef0ebb475855695400a8475b809e35f898c32c9c3908a75dcb1b5b16802bc0b9694ff2450e34ea599bdb35e7fbfc4ac8ad7e4f
7
- data.tar.gz: 66cc4df70435f6f0ccf6dc7601483d1c9b892b9e35d5447ecc6ed3303b86b6758c049fc3136c0e86a54652a6f6c6c23ccd35e69635ac4631a6356bde01ae21ff
6
+ metadata.gz: fbb655bb816b95df9b9cfb7f2718dfaf266039b2e3e478772c3bab2c84669b00af7d967186a1c210eca8b26dccb4aae2b7b6988ca4ddb2894c9fb35717f25846
7
+ data.tar.gz: a0539aeddd100251a5fe1c74127df3e3fcd27ece5aa9b93d6a5847b12878eff277b0cbba2692846050e5101a29f12875d47a1cfa1db863481609a4c0f0e6a785
data/README.md CHANGED
@@ -2,14 +2,16 @@
2
2
 
3
3
  ![Screenshot](screenshot.png)
4
4
 
5
- This is a [Jekyll](http://jekyllrb.com) theme based on [Bootstrap 4](http://getbootstrap.com) which can be used to make a simple, responsive website for a one-day conference or workshop with parallel tracks containing:
5
+ This is a responsive [Jekyll](http://jekyllrb.com) theme based on [Bootstrap 4](http://getbootstrap.com) for conferences. It contains
6
6
 
7
- - program / schedule
8
- - talk and speaker descriptions
9
- - map for directions
7
+ - multiday program / schedule,
8
+ - talk and speaker descriptions,
9
+ - map for directions,
10
+ - realtime live indications during the conference, and
11
+ - supports embedded video streaming or recordings.
10
12
 
11
- All components such as talks, speakers or rooms are represented as collection of files. The schedule is given is defined via a simple structure stored in a [YAML](https://en.wikipedia.org/wiki/YAML) file. There is no need for databases and once generated the website consists only of static files.
12
- The design is easily modifiable and is adapted for mobile uses and printing.
13
+ All components such as talks, speakers or rooms are represented as collection of files. The schedule is given is defined via a simple structure stored in a [YAML](https://en.wikipedia.org/wiki/YAML) file. There is no need for databases and once generated the website consists only of static files. A script and workflows are available for easy import, e.g. of [frab](https://github.com/frab/frab/wiki/Manual#introduction) compatible schedules.
14
+ The design is easily customizable and is adapted for mobile uses and printing.
13
15
 
14
16
  The theme was originally created for the yearly Winterkongress conference of the [Digital Society Switzerland](https://digitale-gesellschaft.ch/). You can see this theme in action here:
15
17
 
@@ -44,11 +46,7 @@ This allows for easier installation and updating as you don't have to manage any
44
46
  theme: jekyll-theme-conference
45
47
  ```
46
48
 
47
- 4. Copy the internationalization file containing the different language strings for this repository to your Jekyll folder:
48
-
49
- - `_data/lang.yml`
50
-
51
- 5. Continue with the _Setup_ section further below to customize the theme and add some content for your conference
49
+ 4. Continue with the _Setup_ section further below to customize the theme and add some content for your conference
52
50
 
53
51
  To update the theme run `bundle update`.
54
52
 
@@ -76,11 +74,7 @@ To install:
76
74
 
77
75
  4. Add `remote_theme: "DigitaleGesellschaft/jekyll-theme-conference@2.0.0"` to your `_config.yml` file. Remove any other `theme:` or `remote_theme:` entry.
78
76
 
79
- 5. Copy the internationalization file containing the different language strings for this repository to your Jekyll folder:
80
-
81
- - `_data/lang.yml`
82
-
83
- 6. Continue with the _Setup_ section further below to customize the theme and add some content for your conference
77
+ 5. Continue with the _Setup_ section further below to customize the theme and add some content for your conference
84
78
 
85
79
 
86
80
  ## Setup
@@ -108,21 +102,41 @@ In order to be up and running simply use the default content of this repository
108
102
 
109
103
  ### Automatic Import
110
104
 
111
- There exists a Python file in this repository, `create_entries.py`, which can be used to import content from a [frab](https://github.com/frab/frab/wiki/Manual#introduction) compatible JSON file or a CSV table and generate the different talk, speakers and room files automatically based on it. Just open your terminal and type `python create_entries.py --help` to show the help and get started.
105
+ There exists a Python file in this repository, `_tools/create_entries.py`, which can be used to import content from a [frab](https://github.com/frab/frab/wiki/Manual#introduction) compatible JSON file (e.g. from [pretalx.com](https://pretalx.com/p/about/)) or a CSV table and generate the different talk, speakers and room files automatically. It has as only dependency [PyYAML](https://pypi.org/project/PyYAML/):
106
+
107
+ 1. Copy the file `_tools/create_entries.py` from this repository
108
+
109
+ 2. Create a virtual environment and activate it
110
+
111
+ ```bash
112
+ python -m venv venv
113
+ source venv/bin/activate
114
+ ```
115
+
116
+ 3. Install PyYAML
117
+
118
+ ```bash
119
+ pip install pyyaml
120
+ ```
121
+
122
+ 4. Execute the script, e.g. to show the help type
123
+
124
+ ```bash
125
+ python _tools/create_entries.py --help
126
+ ```
127
+
112
128
 
113
129
  ### Automatic Build
114
130
 
115
- In case you do not want to install the entire Ruby/Jekyll toolchain on your machine you can make use of [GitHub Actions](https://github.com/features/actions), Github's continuous integration platform. This repository contains an example Github Action configuration file which automatically builds and minimizes the website upon adding a new tag. It then attaches the generated website as package to a release for easy downloading. Simply copy the following file to your repository and adapt it to your needs:
131
+ In case you do not want to install the entire Ruby/Jekyll toolchain on your machine you can make use of [GitHub Actions](https://github.com/features/actions), Github's continuous integration platform. This repository contains multiple example Github Action configuration files in the `_tools/` folder:
116
132
 
117
- - `workflow-example.yml` -> `.github/workflows/main.yml`
133
+ - `build.yml`: automatically builds and minimizes the website upon adding a new tag starting with a `v` (e.g. `v2020.01.01`). It then attaches the generated website as an archive to a release for easy downloading.
134
+ - `test.yml`: automatically tries to build the website upon a new pull request. It can thus be used as status check before merging.
135
+ - `schedule.yml`: automatically generates the schedule and content files when a new pull request contains a `schedule.json` file (see the _Automatic Import_subsection above). Thus, it allows quick updates of the site's content from [pretalx.com](https://pretalx.com/p/about/) exports.
118
136
 
119
- Hidden rooms, speakers, or talks are automatically generated in way containing no content. In order to remove these empty files simply add a file called `delete_hidden.sh` to the root with the following content. It will automatically called by the Github Action workflow to delete the files.
137
+ To get started, simply copy the desired workflow file to your repository and adapt it to your needs:
120
138
 
121
- ```markdown
122
- ---
123
- layout: delete_hidden
124
- ---
125
- ```
139
+ - `_tools/build.yml` -> `.github/workflows/build.yml`
126
140
 
127
141
 
128
142
  ## Configuration
@@ -307,21 +321,23 @@ In order to help users navigating the program during the congress, a _Live_ indi
307
321
 
308
322
  This can be further extended if some of the talks have an associated live stream: Upon clicking one of the live indications a modal will open containing the corresponding live stream embedded. The URL to the live stream has to be set via `live` property in each room (see the _Content_ > _Room_ section below).
309
323
 
310
- In order to activate the functionality the `live` property has to be set containing
324
+ In order to activate the functionality, each day in the `program.yml` file must contain a `date` property (see section _Content_ > _Schedule / Program_ below) and the `live` property has to be set in the configuration file containing
311
325
 
312
- - the date of the day at which the conference takes place (`date`),
313
- - the timezone in which the conference takes place (`timezone`),
314
- - optionally if streaming is enabled (`streaming`) with indications how many minutes early the stream will begin and end, and
326
+ - how long a pause between two consecutive talks has to be for the live indication to pause (`time_stop`),
327
+ - optionally if streaming is enabled (`streaming`) with indications
328
+ + how many minutes the stream goes active before a talk (`time_prepend`),
329
+ + how many minutes the stream stays active after a talk (`time_extend`), and
330
+ + how long a pause between two consecutive talks has to be for the stream to pause (`time_pause`),
315
331
  - optionally a demo mode setting, whereby the JavaScript function cycles through the entire program in five minutes for demonstration purposes (`demo: true`, default: `false`).
316
332
 
317
333
  ```yaml
318
334
  conference:
319
335
  live:
320
- date: 01.01.2020
321
- timezone: GMT+1
336
+ time_stop: 240 # in minutes
322
337
  streaming:
323
- start_early: 15 # in minutes
324
- end_late: 0 # in minutes
338
+ time_pause: 60 # in minutes
339
+ time_prepend: 5 # in minutes
340
+ time_extend: 5 # in minutes
325
341
  demo: false
326
342
  ```
327
343
 
@@ -443,33 +459,52 @@ The actual schedule defining when and in which room a talk takes place is stored
443
459
 
444
460
  ### Schedule / Program
445
461
 
446
- The schedule of the conference linking the talks with the rooms and indicating when each talk talks place and how long it goes is set in the `_data/program.yml` file. It consists of an array of rooms each consisting of a
462
+ The schedule of the conference linking the talks with the rooms and indicating when each talk talks place and how long it goes is set in the `_data/program.yml` file. It contains a list of days, whereby each day contains a list of rooms, whereby each room contains a list of talks.
463
+
464
+ Each day consists of
447
465
 
448
- - `room` name (must correspond to one of the room identifier), and
449
- - an array of `talks` which also can be empty `[]`.
466
+ - a list of rooms (`rooms`) in which talks are taking place on that day
467
+ - optionally, the day's `name`, e.g. the weekday
468
+ - optionally, the short form of the day's name (`abbr`), and
469
+ - optionally only if no live indications are active, a `date` in the format `YYYY-MM-DD`.
450
470
 
451
- The order of the room in the file defines the order of the rooms on the website (program and room listings). Each talk in the array consists of
471
+ Each room consists of
472
+
473
+ - the room's `name` (must correspond to one of the room identifier), and
474
+ - a list of talks (`talks`) which also can be empty `[]`.
475
+
476
+ The order of the rooms in the list defines the order of the rooms as shown in the schedule on the program page. For the live streaming or the room overview the order of the rooms is alphabetical but can be adapted via the [main configuration file](https://jekyllrb.com/docs/collections/#sort-by-front-matter-key).
477
+
478
+ Each talk consists of
452
479
 
453
480
  - a `name` (must correspond to one of the talk identifier),
454
481
  - a starting time `time_start` given as `H:M` ([`strftime`](http://www.strfti.me) formated), and
455
482
  - an end time `time_end`.
456
483
 
457
- The array should (manually) be ordered by time. Currently talks can only take place on the same day and multi-day conferences are not supported.
484
+ The list of talks should (manually) be ordered by time, i.e. the first occurring talk should be listed first.
458
485
 
459
486
  Example:
460
487
 
461
488
  ```yaml
462
- - room: Room A
463
- talks:
464
- - name: Vim Impetus Placerat Cotidieque Ad
465
- time_start: '12:00'
466
- time_end: '12:45'
467
- - name: Condimentum Vitae Sapien Pellentesque
468
- time_start: '12:45'
469
- time_end: '13:30'
470
- - room: Room B
471
- talks:
472
- ...
489
+ days:
490
+ - name: Monday
491
+ abbr: Mo
492
+ date: 2020-01-31
493
+ rooms:
494
+ - name: Room A
495
+ talks:
496
+ - name: Vim Impetus Placerat Cotidieque Ad
497
+ time_start: '12:00'
498
+ time_end: '12:45'
499
+ - name: Condimentum Vitae Sapien Pellentesque
500
+ time_start: '12:45'
501
+ time_end: '13:30'
502
+
503
+ - name: Room B
504
+ talks:
505
+ - name: Arcu Non Odio
506
+ time_start: '12:00'
507
+ time_end: '13:00'
473
508
  ```
474
509
 
475
510
  ### Talks
@@ -517,7 +552,7 @@ Links are used at different location throughout the theme: They can either be us
517
552
  Additionally, a navigation bar or main landing page link can also have the following properties:
518
553
 
519
554
  - `menu` containing another list of links. This creates a dropdown menu of multiple sublinks. The sublinks have the same properties as regular links, or
520
- - `live` making the link only visible during the conference and adds a live indication. The `name` property can be omitted. If streaming is enabled and any URL property is omitted, a click on the link will open the streaming modal (see section _Live Indications_ above).
555
+ - `live` making the link only visible during the conference and adds a live indication. The `name` property can be omitted. Using the optional `name_inactive` property shows a placeholder text while the conference is **not** live. If streaming is enabled and any URL property is omitted, a click on the link will open the streaming modal (see section _Live Indications_ above).
521
556
 
522
557
  Using the `file:` indicator, the relative address is automatically set as well as the icon. Using the `video:` indicator, the link is automatically configured to open in an iframe with a corresponding title and the icon is set.
523
558
 
@@ -1,14 +1,21 @@
1
- window.conference.live = (function() {
2
- {%- include partials/get_conf_time.html -%}
3
- {%- assign time_start = conf_start -%}
4
- {%- assign time_end = conf_end -%}
5
- {%- include partials/get_timestamp.html -%}
6
-
7
- let confStart = {{ timestamp_start }};
8
- let confEnd = {{ timestamp_end }};
9
- let confDur = confEnd - confStart;
1
+ window.conference.live = (function() {
2
+ {% assign d = site.data.program.days | first -%}
3
+ {%- include partials/get_day_time.html -%}
4
+ {%- assign t = day_start_talk -%}
5
+
6
+ {%- include partials/get_talk_timestamp.html -%}
7
+ {%- assign conf_start = timestamp_start -%}
8
+
9
+ {%- assign d = site.data.program.days | last -%}
10
+ {%- include partials/get_day_time.html -%}
11
+ {%- assign t = day_end_talk -%}
10
12
 
11
- let talkAnnounce = 120; // in minutes
13
+ {%- include partials/get_talk_timestamp.html -%}
14
+ {%- assign conf_end = timestamp_end -%}
15
+
16
+ let confStart = {{ conf_start }};
17
+ let confEnd = {{ conf_end }};
18
+ let confDur = confEnd - confStart;
12
19
 
13
20
  let freezeTime = false;
14
21
  let timeFrozen = 0;
@@ -22,7 +29,7 @@
22
29
  let demoEnd = confEnd + confDur/durDemo*durPause;
23
30
 
24
31
  let liveTimer;
25
- let streamTimer;
32
+ let streamVideoTimer;
26
33
  let streamInfoTimer;
27
34
 
28
35
  let mod = function (n, m) {
@@ -80,10 +87,22 @@
80
87
  startUpdate();
81
88
  };
82
89
 
83
- let setTime = function (newTime) {
90
+ let setTime = function (newTime, newDay=1) {
84
91
  pauseTime();
85
92
 
86
- let d = new Date(confStart * 1000);
93
+ let dayIdx;
94
+ if (Number.isInteger(newDay)) {
95
+ dayIdx = newDay-1;
96
+ }
97
+ else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(newDay)) {
98
+ dayIdx = data.days.find(o => o.name === newDay);
99
+ }
100
+ else {
101
+ dayIdx = data.days.find(o => o.name === newDay);
102
+ }
103
+ newDate = data.days[dayIdx].date;
104
+
105
+ let d = new Date(newDate);
87
106
  newTime = newTime.split(':');
88
107
  d.setHours(newTime[0], newTime[1]);
89
108
 
@@ -92,12 +111,13 @@
92
111
  update();
93
112
  };
94
113
 
95
- let getTime = function (tConvert = time()) {
114
+ let getTime = function (tConvert=time()) {
96
115
  let d = new Date(tConvert * 1000);
116
+ let dStr = d.toISOString().slice(0,10);
97
117
  let h = d.getHours();
98
118
  let m = d.getMinutes();
99
119
 
100
- return h + ":" + (m < 10 ? "0" : "") + m;
120
+ return dStr +" "+ h +":"+ (m < 10 ? "0" : "") + m;
101
121
  };
102
122
 
103
123
  let timeUnit = function () {
@@ -145,32 +165,38 @@
145
165
  let livePast = document.getElementsByClassName('live-past');
146
166
 
147
167
  for (let i = 0; i < liveShow.length; i++) {
148
- let tStart = liveShow[i].dataset.start;
149
- let tEnd = liveShow[i].dataset.end;
168
+ let tStarts = liveShow[i].dataset.start.split(',');
169
+ let tEnds = liveShow[i].dataset.end.split(',');
150
170
 
151
- if (tNow >= tStart && tNow < tEnd) {
152
- // Show when active
153
- liveShow[i].classList.remove('d-none');
154
- }
155
- else if (!liveShow[i].classList.contains('d-none')) {
156
- // Hide otherwise
157
- liveShow[i].classList.add('d-none');
171
+ for (let k = 0; k < tStarts.length; k++) {
172
+ if (tNow >= tStarts[k] && tNow < tEnds[k]) {
173
+ // Show when active
174
+ liveShow[i].classList.remove('d-none');
175
+ break;
176
+ }
177
+ else if (!liveShow[i].classList.contains('d-none')) {
178
+ // Hide otherwise
179
+ liveShow[i].classList.add('d-none');
180
+ }
158
181
  }
159
182
  }
160
183
 
161
184
  for (let i = 0; i < liveHide.length; i++) {
162
- let tStart = liveHide[i].dataset.start;
163
- let tEnd = liveHide[i].dataset.end;
164
-
165
- if (tNow >= tStart && tNow < tEnd) {
166
- // Hide when active
167
- if (!liveHide[i].classList.contains('d-none')) {
168
- liveHide[i].classList.add('d-none');
185
+ let tStarts = liveHide[i].dataset.start.split(',');
186
+ let tEnds = liveHide[i].dataset.end.split(',');
187
+
188
+ for (let k = 0; k < tStarts.length; k++) {
189
+ if (tNow >= tStarts[k] && tNow < tEnds[k]) {
190
+ // Hide when active
191
+ if (!liveHide[i].classList.contains('d-none')) {
192
+ liveHide[i].classList.add('d-none');
193
+ break;
194
+ }
195
+ }
196
+ else {
197
+ // Show otherwise
198
+ liveHide[i].classList.remove('d-none');
169
199
  }
170
- }
171
- else {
172
- // Show otherwise
173
- liveHide[i].classList.remove('d-none');
174
200
  }
175
201
  }
176
202
 
@@ -281,98 +307,151 @@
281
307
  };
282
308
 
283
309
  {% if site.conference.live.streaming -%}
284
- {% include js/conference-data.js %}
310
+ let streamPause = {{ site.conference.live.streaming.time_pause | default: 60 }}; // in minutes
311
+ let streamPrepend = {{ site.conference.live.streaming.time_prepend | default: 5 }}; // in minutes
312
+ let streamExtend = {{ site.conference.live.streaming.time_extend | default: 5 }}; // in minutes
285
313
 
286
314
  let streamModal;
315
+ let data;
287
316
 
288
317
  let getRoom = function (roomName) {
289
- if (roomName in rooms) {
290
- return rooms[roomName];
318
+ if (roomName in data.rooms) {
319
+ return data.rooms[roomName];
291
320
  }
292
321
  else {
293
- return rooms[Object.keys(rooms)[0]];
322
+ return data.rooms[Object.keys(data.rooms)[0]];
294
323
  }
295
324
  };
296
325
 
297
- let preStartStream = function (roomName) {
298
- let room = getRoom(roomName);
299
-
300
- streamModal.find('iframe').attr('src', '');
301
- streamModal.find('iframe').addClass('d-none');
302
- streamModal.find('#stream-placeholder > div').text('{{ site.data.lang[site.conference.lang].live.pre_stream | default: "Live stream has not started yet." }}');
303
- streamModal.find('#stream-placeholder').addClass('d-flex');
326
+ let getNextTalk = function (roomName) {
327
+ let timeNow = time();
328
+ let talksHere = data.talks[roomName];
304
329
 
305
- if (typeof streamTimer !== "undefined") {
306
- clearInterval(streamTimer);
330
+ if (typeof talksHere !== "undefined") {
331
+ if (timeNow < talksHere[talksHere.length-1].end) {
332
+ for (var i = 0; i < talksHere.length; i++) {
333
+ if (timeNow < talksHere[i].end) {
334
+ return talksHere[i];
335
+ }
336
+ }
337
+ }
307
338
  }
308
- if (!freezeTime) {
309
- streamTimer = setTimeout(activeStream, delayStart(room.start) * 1000, roomName);
339
+ return false;
340
+ };
341
+
342
+ let getNextPause = function (roomName) {
343
+ let timeNow = time();
344
+ let talksHere = data.talks[roomName];
345
+
346
+ if (typeof talksHere !== "undefined") {
347
+ if (timeNow < talksHere[talksHere.length-1].end) {
348
+ for (var i = 1; i < talksHere.length; i++) {
349
+ if (timeNow < talksHere[i].start && streamPause*60 <= talksHere[i].start - talksHere[i-1].end) {
350
+ return {
351
+ 'start': talksHere[i-1].end,
352
+ 'end': talksHere[i].start,
353
+ };
354
+ }
355
+ }
356
+ }
310
357
  }
358
+ return false;
311
359
  };
312
360
 
313
- let activeStream = function (roomName) {
314
- let room = getRoom(roomName);
361
+ let setStreamContent = function (content) {
362
+ streamModal.find('iframe').attr('src', '');
363
+ streamModal.find('iframe').addClass('d-none');
364
+ streamModal.find('#stream-placeholder > div').text(content);
365
+ streamModal.find('#stream-placeholder').addClass('d-flex');
366
+ };
315
367
 
316
- streamModal.find('iframe').attr('src', room.href);
368
+ let setStreamSrc = function (href) {
369
+ streamModal.find('iframe').attr('src', href);
317
370
  streamModal.find('#stream-placeholder').addClass('d-none').removeClass('d-flex');
318
371
  streamModal.find('iframe').removeClass('d-none');
372
+ };
319
373
 
320
- if (typeof streamTimer !== "undefined") {
321
- clearInterval(streamTimer);
322
- }
323
- if (!freezeTime) {
324
- streamTimer = setTimeout(postEndStream, delayStart(room.end) * 1000, roomName);
374
+ let setStreamVideo = function (roomName) {
375
+ let timeNow = time();
376
+
377
+ let talksHere = data.talks[roomName];
378
+ let roomStart = talksHere[0].start;
379
+ let roomEnd = talksHere[talksHere.length-1].end;
380
+
381
+ if (typeof streamVideoTimer !== "undefined") {
382
+ clearInterval(streamVideoTimer);
325
383
  }
326
- };
327
384
 
328
- let postEndStream = function (roomName) {
329
- let room = getRoom(roomName);
385
+ // Conference not yet started
386
+ if (timeNow < roomStart - streamPrepend*60) {
387
+ setStreamContent('{{ site.data.lang[site.conference.lang].live.pre_stream | default: "Live stream has not started yet." }}');
330
388
 
331
- streamModal.find('iframe').attr('src', '');
332
- streamModal.find('iframe').addClass('d-none');
333
- streamModal.find('#stream-placeholder > div').text('{{ site.data.lang[site.conference.lang].live.post_stream | default: "Live stream has ended." }}');
334
- streamModal.find('#stream-placeholder').addClass('d-flex');
389
+ if (!freezeTime) {
390
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomStart - streamPrepend*60) * 1000, roomName);
391
+ }
392
+ }
335
393
 
336
- if (typeof streamTimer !== "undefined") {
337
- clearInterval(streamTimer);
394
+ // Conference is over
395
+ else if (timeNow > roomEnd + streamExtend*60) {
396
+ setStreamContent('{{ site.data.lang[site.conference.lang].live.post_stream | default: "Live stream has ended." }}');
397
+
398
+ if (!freezeTime && demo) {
399
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomEnd - streamPrepend*60) * 1000, roomName);
400
+ }
338
401
  }
339
- if (!freezeTime && demo) {
340
- streamTimer = setTimeout(preStartStream, delayStart(demoStart) * 1000, roomName);
402
+
403
+ // Conference ongoing
404
+ else {
405
+ let pauseNext = getNextPause(roomName);
406
+
407
+ // Currently stream is paused
408
+ if (pauseNext && timeNow >= pauseNext.start + streamExtend*60 && timeNow <= pauseNext.end - streamPrepend*60) {
409
+ setStreamContent('{{ site.data.lang[site.conference.lang].live.pause_stream | default: "Live stream is currently paused." }}');
410
+
411
+ if (!freezeTime) {
412
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(pauseNext.end - streamPrepend*60) * 1000, roomName);
413
+ }
414
+ }
415
+ // Currently a talk is active
416
+ else {
417
+ let room = getRoom(roomName);
418
+ setStreamSrc(room.href);
419
+
420
+ if (!freezeTime) {
421
+ if (pauseNext) {
422
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(pauseNext.start + streamExtend*60) * 1000, roomName);
423
+ }
424
+ else {
425
+ streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomEnd + streamExtend*60) * 1000, roomName);
426
+ }
427
+ }
428
+ }
341
429
  }
342
430
  };
343
431
 
344
432
  let setStreamInfo = function (roomName) {
345
433
  let timeNow = time();
346
- let talksHere = talks[roomName];
347
- let talkNow;
434
+ let talkNext = getNextTalk(roomName);
348
435
 
349
436
  if (typeof streamInfoTimer !== "undefined") {
350
437
  clearInterval(streamInfoTimer);
351
438
  }
352
439
 
353
- if (typeof talksHere !== "undefined") {
354
- if (timeNow < talksHere[talksHere.length-1].end) {
355
- for (var i = 0; i < talksHere.length; i++) {
356
- if (timeNow < talksHere[i].end && timeNow >= talksHere[i].start - talkAnnounce*60) {
357
- talkNow = talksHere[i];
358
- break;
359
- }
360
- }
361
- }
362
- }
363
-
364
- if (typeof talkNow !== "undefined") {
365
- document.getElementById('stream-info').dataset.time = talkNow.start;
366
- document.getElementById('stream-info-time').dataset.time = talkNow.start;
440
+ if (timeNow >= talkNext.start - streamPause*60) {
441
+ document.getElementById('stream-info').dataset.time = talkNext.start;
442
+ document.getElementById('stream-info-time').dataset.time = talkNext.start;
367
443
  updateLive();
368
444
 
369
- streamModal.find('#stream-info-color').addClass('border-soft-' + talkNow.color);
445
+ streamModal.find('#stream-info-color').removeClass(function (index, className) {
446
+ return (className.match(/(^|\s)border-soft-\S+/g) || []).join(' ');
447
+ });
448
+ streamModal.find('#stream-info-color').addClass('border-soft-' + talkNext.color);
370
449
 
371
- streamModal.find('#stream-info-talk').text(talkNow.name).attr('href', talkNow.href);
450
+ streamModal.find('#stream-info-talk').text(talkNext.name).attr('href', talkNext.href);
372
451
 
373
452
  let speakerStr = '';
374
- for (var i = 0; i < talkNow.speakers.length; i++) {
375
- let speaker = speakers[talkNow.speakers[i]];
453
+ for (var i = 0; i < talkNext.speakers.length; i++) {
454
+ let speaker = data.speakers[talkNext.speakers[i]];
376
455
  if (speaker.href == '') {
377
456
  speakerStr += speaker.name +', '
378
457
  }
@@ -386,35 +465,37 @@
386
465
  streamModal.find('#stream-info').removeClass('d-none');
387
466
 
388
467
  if (!freezeTime) {
389
- streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNow.end) * 1000, roomName);
468
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNext.end) * 1000, roomName);
390
469
  }
391
470
  }
392
471
  else {
393
472
  streamModal.find('#stream-info').addClass('d-none');
473
+
474
+ if (!freezeTime) {
475
+ if (talkNext) {
476
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNext.start - streamPause*60) * 1000, roomName);
477
+ }
478
+ else if (demo) {
479
+ let talksHere = data.talks[roomName];
480
+ streamInfoTimer = setTimeout(setStreamInfo, delayStart(talksHere[0].start - streamPrepend*60) * 1000, roomName);
481
+ }
482
+ }
394
483
  }
395
484
  };
396
485
 
397
486
  let setStream = function (roomName) {
398
487
  streamModal.find('.modal-footer .btn').removeClass('active');
399
- streamModal.find('#stream-select').selectedIndex = -1;
488
+ streamModal.find('#stream-select').val(0);
400
489
 
490
+ // Recover room name in case of empty default
401
491
  let room = getRoom(roomName);
402
492
  roomName = room.name;
403
- let tNow = time();
404
493
 
405
- if (tNow < room.start) {
406
- preStartStream(roomName);
407
- }
408
- else if (tNow > room.end) {
409
- postEndStream(roomName);
410
- }
411
- else {
412
- activeStream(roomName);
413
- }
494
+ setStreamVideo(roomName);
414
495
  setStreamInfo(roomName);
415
496
 
416
497
  streamModal.find('#stream-button' + room.id).addClass('active');
417
- streamModal.find('#stream-select').selectedIndex = room.id;
498
+ streamModal.find('#stream-select').val(room.id);
418
499
  };
419
500
 
420
501
  let updateStream = function () {
@@ -429,8 +510,8 @@
429
510
  };
430
511
 
431
512
  let stopUpdateStream = function () {
432
- if (typeof streamTimer !== "undefined") {
433
- clearInterval(streamTimer);
513
+ if (typeof streamVideoTimer !== "undefined") {
514
+ clearInterval(streamVideoTimer);
434
515
  }
435
516
  if (typeof streamInfoTimer !== "undefined") {
436
517
  clearInterval(streamInfoTimer);
@@ -446,6 +527,7 @@
446
527
  let setupStream = function () {
447
528
  streamModal = $('#stream-modal');
448
529
 
530
+ // configure modal opening buttons
449
531
  streamModal.on('show.bs.modal', function (event) {
450
532
  let button = $(event.relatedTarget);
451
533
  let roomName = button.data('room');
@@ -455,6 +537,7 @@
455
537
  hideModal(event);
456
538
  });
457
539
 
540
+ // configure room selection buttons in modal
458
541
  streamModal.find('.modal-footer .btn').on('click', function(event) {
459
542
  event.preventDefault();
460
543
 
@@ -462,12 +545,18 @@
462
545
  setStream(roomName);
463
546
  });
464
547
 
548
+ // configure room selection menu in modal
465
549
  streamModal.find('#stream-select').on('change', function(event) {
466
550
  event.preventDefault();
467
551
 
468
552
  let roomName = $(this).children('option:selected').text();
469
553
  setStream(roomName);
470
554
  });
555
+
556
+ // load data
557
+ $.getJSON('{{ site.baseurl }}/assets/js/data.json', function(json) {
558
+ data = json;
559
+ });
471
560
  };
472
561
 
473
562
  let setup = function () {