jekyll-theme-conference 2.5.1 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +89 -54
- data/_includes/js/conference-live.js +308 -104
- data/_includes/js/conference-modal.js +32 -15
- data/_includes/js/conference-program.js +48 -0
- data/_includes/js/conference.js +11 -11
- data/_includes/js/jquery-3.5.1.min.js +2 -0
- data/_includes/partials/checks.html +66 -31
- data/_includes/partials/footer.html +6 -22
- data/_includes/partials/get_day_hash.html +20 -0
- data/_includes/partials/get_day_time.html +19 -0
- data/_includes/partials/get_link.html +56 -64
- data/_includes/partials/get_link_types.html +14 -14
- data/_includes/partials/get_live_timestamps.html +49 -0
- data/_includes/partials/get_main_category.html +9 -9
- data/_includes/partials/get_talk_time.html +7 -7
- data/_includes/partials/get_talk_timestamp.html +4 -0
- data/_includes/partials/get_time_pronoun.html +6 -0
- data/_includes/partials/header.html +15 -12
- data/_includes/partials/info_bar.html +22 -22
- data/_includes/partials/list_categories.html +2 -2
- data/_includes/partials/list_speakers.html +14 -18
- data/_includes/partials/list_sub_categories.html +7 -7
- data/_includes/partials/modal_link.html +21 -0
- data/_includes/partials/{live-modal.html → modal_live.html} +20 -9
- data/_includes/partials/navbar.html +33 -32
- data/_includes/partials/navbar_rooms.html +21 -20
- data/_includes/partials/{live_button.html → show_live_button.html} +6 -8
- data/_includes/partials/show_room.html +5 -5
- data/_includes/partials/show_talk.html +3 -3
- data/_includes/partials/show_talk_duration.html +1 -1
- data/_includes/partials/show_talk_time.html +16 -2
- data/_layouts/data.html +91 -0
- data/_layouts/home.html +54 -6
- data/_layouts/program.html +179 -148
- data/_layouts/room.html +42 -38
- data/_layouts/speaker.html +41 -39
- data/_layouts/talk-overview.html +83 -53
- data/_layouts/talk.html +38 -39
- data/_sass/conference.scss +29 -7
- data/assets/icons/live.svg +33 -57
- data/assets/js/data.json +3 -0
- data/assets/js/main.js +0 -6
- metadata +14 -8
- data/_includes/js/jquery-3.2.1.slim.min.js +0 -4
- data/_includes/partials/get_conf_time.html +0 -54
- data/_includes/partials/get_timestamp.html +0 -4
- data/_layouts/delete_hidden.html +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77630ecb9f0ae81a9f53c52bf08d86fda4be6a75e92668e95f0a5f48ed270748
|
4
|
+
data.tar.gz: 32ff767339f60c06bff39b86cbf2d1126f1c149ab797a26f1b1ec9fcdfc49223
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b0edcba1a9c41fc6365312f1cba5056ce66dd351283a9af19266d558fe27dac3b01d2fa0f83a0567eacc293f91489b1af7922e868325f23b19996a6608b0ec9
|
7
|
+
data.tar.gz: 18c8a4f2e500b45d1e4ddba54eeb466b4b2b3d7bf9171554c15403f8e58812f7f06bd06f1864e25cf9b792811fcd6b0ad16c1448088d727b6c57ac1246dea23b
|
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)
|
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
|
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.
|
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.
|
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
|
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
|
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
|
-
- `
|
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
|
-
|
137
|
+
To get started, simply copy the desired workflow file to your repository and adapt it to your needs:
|
120
138
|
|
121
|
-
|
122
|
-
---
|
123
|
-
layout: delete_hidden
|
124
|
-
---
|
125
|
-
```
|
139
|
+
- `_tools/build.yml` -> `.github/workflows/build.yml`
|
126
140
|
|
127
141
|
|
128
142
|
## Configuration
|
@@ -201,11 +215,6 @@ conference:
|
|
201
215
|
|
202
216
|
The navigation bar is located at the top and visible on every site. On the right it show the title of the website (`site.title`) followed by the links listed under the `links` property of the `navigation` property. See the _Content_ > _Links_ section below for the available properties per link.
|
203
217
|
|
204
|
-
Additionally, a navigation bar link can also have the following properties:
|
205
|
-
|
206
|
-
- `menu` containing another list of links. This creates a dropdown menu of multiple sublinks. The sublinks have the same properties as regular links (see the _Content_ > _Links_ section), or
|
207
|
-
- `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_ below).
|
208
|
-
|
209
218
|
Example:
|
210
219
|
|
211
220
|
```yaml
|
@@ -312,21 +321,23 @@ In order to help users navigating the program during the congress, a _Live_ indi
|
|
312
321
|
|
313
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).
|
314
323
|
|
315
|
-
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
|
316
325
|
|
317
|
-
-
|
318
|
-
-
|
319
|
-
|
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`),
|
320
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`).
|
321
332
|
|
322
333
|
```yaml
|
323
334
|
conference:
|
324
335
|
live:
|
325
|
-
|
326
|
-
timezone: GMT+1
|
336
|
+
time_stop: 240 # in minutes
|
327
337
|
streaming:
|
328
|
-
|
329
|
-
|
338
|
+
time_pause: 60 # in minutes
|
339
|
+
time_prepend: 5 # in minutes
|
340
|
+
time_extend: 5 # in minutes
|
330
341
|
demo: false
|
331
342
|
```
|
332
343
|
|
@@ -448,33 +459,52 @@ The actual schedule defining when and in which room a talk takes place is stored
|
|
448
459
|
|
449
460
|
### Schedule / Program
|
450
461
|
|
451
|
-
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
|
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
|
465
|
+
|
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`.
|
452
470
|
|
453
|
-
|
454
|
-
- an array of `talks` which also can be empty `[]`.
|
471
|
+
Each room consists of
|
455
472
|
|
456
|
-
|
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
|
457
479
|
|
458
480
|
- a `name` (must correspond to one of the talk identifier),
|
459
481
|
- a starting time `time_start` given as `H:M` ([`strftime`](http://www.strfti.me) formated), and
|
460
482
|
- an end time `time_end`.
|
461
483
|
|
462
|
-
The
|
484
|
+
The list of talks should (manually) be ordered by time, i.e. the first occurring talk should be listed first.
|
463
485
|
|
464
486
|
Example:
|
465
487
|
|
466
488
|
```yaml
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
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'
|
478
508
|
```
|
479
509
|
|
480
510
|
### Talks
|
@@ -519,6 +549,11 @@ Links are used at different location throughout the theme: They can either be us
|
|
519
549
|
+ pointing to a file uploaded to the `/documents` folder (for talks `/documents/slides`, for speakers `/documents/bio`): `file:`
|
520
550
|
+ pointing to an external video: `video:`
|
521
551
|
|
552
|
+
Additionally, a navigation bar or main landing page link can also have the following properties:
|
553
|
+
|
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
|
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).
|
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
|
|
524
559
|
Example:
|
@@ -1,11 +1,20 @@
|
|
1
|
-
|
2
|
-
{
|
3
|
-
{%-
|
4
|
-
{%- assign
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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 -%}
|
12
|
+
|
13
|
+
{%- include partials/get_talk_timestamp.html -%}
|
14
|
+
{%- assign conf_end = timestamp_end -%}
|
15
|
+
|
16
|
+
let confStart = {{ conf_start }};
|
17
|
+
let confEnd = {{ conf_end }};
|
9
18
|
let confDur = confEnd - confStart;
|
10
19
|
|
11
20
|
let freezeTime = false;
|
@@ -20,7 +29,8 @@
|
|
20
29
|
let demoEnd = confEnd + confDur/durDemo*durPause;
|
21
30
|
|
22
31
|
let liveTimer;
|
23
|
-
let
|
32
|
+
let streamVideoTimer;
|
33
|
+
let streamInfoTimer;
|
24
34
|
|
25
35
|
let mod = function (n, m) {
|
26
36
|
return ((n % m) + m) % m;
|
@@ -77,10 +87,22 @@
|
|
77
87
|
startUpdate();
|
78
88
|
};
|
79
89
|
|
80
|
-
let setTime = function (newTime) {
|
90
|
+
let setTime = function (newTime, newDay=1) {
|
81
91
|
pauseTime();
|
82
92
|
|
83
|
-
let
|
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);
|
84
106
|
newTime = newTime.split(':');
|
85
107
|
d.setHours(newTime[0], newTime[1]);
|
86
108
|
|
@@ -89,12 +111,13 @@
|
|
89
111
|
update();
|
90
112
|
};
|
91
113
|
|
92
|
-
let getTime = function (tConvert
|
114
|
+
let getTime = function (tConvert=time()) {
|
93
115
|
let d = new Date(tConvert * 1000);
|
116
|
+
let dStr = d.toISOString().slice(0,10);
|
94
117
|
let h = d.getHours();
|
95
118
|
let m = d.getMinutes();
|
96
119
|
|
97
|
-
return
|
120
|
+
return dStr +" "+ h +":"+ (m < 10 ? "0" : "") + m;
|
98
121
|
};
|
99
122
|
|
100
123
|
let timeUnit = function () {
|
@@ -138,34 +161,120 @@
|
|
138
161
|
let tNow = time();
|
139
162
|
let liveShow = document.getElementsByClassName('live-show');
|
140
163
|
let liveHide = document.getElementsByClassName('live-hide');
|
164
|
+
let liveTime = document.getElementsByClassName('live-time');
|
165
|
+
let livePast = document.getElementsByClassName('live-past');
|
141
166
|
|
142
167
|
for (let i = 0; i < liveShow.length; i++) {
|
143
|
-
let
|
144
|
-
let
|
168
|
+
let tStarts = liveShow[i].dataset.start.split(',');
|
169
|
+
let tEnds = liveShow[i].dataset.end.split(',');
|
170
|
+
|
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
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
for (let i = 0; i < liveHide.length; i++) {
|
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');
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
for (let i = 0; i < liveTime.length; i++) {
|
204
|
+
let t = liveTime[i].dataset.time;
|
205
|
+
if (typeof t == "undefined") {
|
206
|
+
break;
|
207
|
+
}
|
208
|
+
let tRel = tNow - t;
|
145
209
|
|
146
|
-
|
147
|
-
|
148
|
-
|
210
|
+
let tStr;
|
211
|
+
if (tRel >= -60 && tRel < 0) {
|
212
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.soon | default: "soon" }}';
|
149
213
|
}
|
150
|
-
else if (
|
151
|
-
|
152
|
-
liveShow[i].classList.add('d-none');
|
214
|
+
else if (tRel >= 0 && tRel < 60) {
|
215
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.now | default: "now" }}';
|
153
216
|
}
|
217
|
+
else {
|
218
|
+
if (tRel < 0) {
|
219
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.in | default: "in" }} ';
|
220
|
+
}
|
221
|
+
else {
|
222
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.since | default: "since" }} ';
|
223
|
+
}
|
224
|
+
tRel = Math.abs(tRel);
|
225
|
+
|
226
|
+
let dWeeks = Math.floor(tRel / (7*24*60*60));
|
227
|
+
let dDays = Math.floor(tRel / (24*60*60));
|
228
|
+
let dHours = Math.floor(tRel / (60*60));
|
229
|
+
let dMins = Math.floor(tRel / (60));
|
230
|
+
if (dWeeks > 4) {
|
231
|
+
break;
|
232
|
+
}
|
233
|
+
else if (dWeeks > 1) {
|
234
|
+
tStr += dWeeks +' {{ site.data.lang[site.conference.lang].live.time.weeks | default: "weeks" }}';
|
235
|
+
}
|
236
|
+
else if (dWeeks == 1) {
|
237
|
+
tStr += '1 {{ site.data.lang[site.conference.lang].live.time.week | default: "week" }}';
|
238
|
+
}
|
239
|
+
else if (dDays > 1) {
|
240
|
+
tStr += dDays +' {{ site.data.lang[site.conference.lang].live.time.days | default: "days" }}';
|
241
|
+
}
|
242
|
+
else if (dDays == 1) {
|
243
|
+
tStr += '1 {{ site.data.lang[site.conference.lang].live.time.day | default: "day" }}';
|
244
|
+
}
|
245
|
+
else if (dHours > 1) {
|
246
|
+
tStr += dHours +' {{ site.data.lang[site.conference.lang].live.time.hours | default: "hours" }}';
|
247
|
+
}
|
248
|
+
else if (dHours == 1) {
|
249
|
+
tStr += '1 {{ site.data.lang[site.conference.lang].live.time.hour | default: "hour" }}';
|
250
|
+
}
|
251
|
+
else if (dMins > 1) {
|
252
|
+
tStr += dMins +' {{ site.data.lang[site.conference.lang].live.time.minutes | default: "minutes" }}';
|
253
|
+
}
|
254
|
+
else {
|
255
|
+
tStr += '1 {{ site.data.lang[site.conference.lang].live.time.minute | default: "minute" }}';
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
liveTime[i].innerHTML = tStr;
|
154
260
|
}
|
155
261
|
|
156
|
-
for (let i = 0; i <
|
157
|
-
let
|
158
|
-
|
262
|
+
for (let i = 0; i < livePast.length; i++) {
|
263
|
+
let t = livePast[i].dataset.time;
|
264
|
+
if (typeof t == "undefined") {
|
265
|
+
break;
|
266
|
+
}
|
267
|
+
let tRel = tNow - t;
|
159
268
|
|
160
|
-
if (
|
161
|
-
//
|
162
|
-
if (!
|
163
|
-
|
269
|
+
if (tRel < 0) {
|
270
|
+
// Grey out when in past
|
271
|
+
if (!livePast[i].classList.contains('text-secondary')) {
|
272
|
+
livePast[i].classList.add('text-secondary');
|
164
273
|
}
|
165
274
|
}
|
166
275
|
else {
|
167
|
-
// Show otherwise
|
168
|
-
|
276
|
+
// Show normal otherwise
|
277
|
+
livePast[i].classList.remove('text-secondary');
|
169
278
|
}
|
170
279
|
}
|
171
280
|
|
@@ -198,111 +307,195 @@
|
|
198
307
|
};
|
199
308
|
|
200
309
|
{% if site.conference.live.streaming -%}
|
201
|
-
|
202
|
-
let
|
203
|
-
|
204
|
-
{%- assign room = site.rooms | where: 'name', r.room | first -%}
|
205
|
-
{%- if room.live -%}
|
206
|
-
|
207
|
-
{%- assign t = r.talks | first -%}
|
208
|
-
{%- include partials/get_talk_time.html -%}
|
209
|
-
{%- assign time_start = talk_start -%}
|
210
|
-
{%- assign time_end = talk_end -%}
|
211
|
-
{%- include partials/get_timestamp.html -%}
|
212
|
-
|
213
|
-
{%- assign offset_start = site.conference.live.streaming.start_early | default: 0 -%}
|
214
|
-
{%- assign room_ts_start = offset_start | times: -60 | plus: timestamp_start -%}
|
215
|
-
|
216
|
-
{%- assign t = r.talks | last -%}
|
217
|
-
{%- include partials/get_talk_time.html -%}
|
218
|
-
{%- assign time_start = talk_start -%}
|
219
|
-
{%- assign time_end = talk_end -%}
|
220
|
-
{%- include partials/get_timestamp.html -%}
|
221
|
-
|
222
|
-
{%- assign offset_end = site.conference.live.streaming.end_late | default: 0 -%}
|
223
|
-
{%- assign room_ts_end = offset_end | times: 60 | plus: timestamp_end -%}
|
224
|
-
|
225
|
-
"{{ room.name }}": {
|
226
|
-
"id": {{ forloop.index }},
|
227
|
-
"href": "{{ room.live }}",
|
228
|
-
"start": {{ room_ts_start }},
|
229
|
-
"end": {{ room_ts_end }}
|
230
|
-
},
|
231
|
-
{%- endif -%}
|
232
|
-
{%- endfor -%}
|
233
|
-
};
|
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
|
234
313
|
|
235
314
|
let streamModal;
|
315
|
+
let data;
|
236
316
|
|
237
317
|
let getRoom = function (roomName) {
|
238
|
-
if (roomName in rooms) {
|
239
|
-
return rooms[roomName];
|
318
|
+
if (roomName in data.rooms) {
|
319
|
+
return data.rooms[roomName];
|
240
320
|
}
|
241
321
|
else {
|
242
|
-
return rooms[Object.keys(rooms)[0]];
|
322
|
+
return data.rooms[Object.keys(data.rooms)[0]];
|
243
323
|
}
|
244
324
|
};
|
245
325
|
|
246
|
-
let
|
247
|
-
let
|
326
|
+
let getNextTalk = function (roomName) {
|
327
|
+
let timeNow = time();
|
328
|
+
let talksHere = data.talks[roomName];
|
329
|
+
|
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
|
+
}
|
338
|
+
}
|
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
|
+
}
|
357
|
+
}
|
358
|
+
return false;
|
359
|
+
};
|
248
360
|
|
361
|
+
let setStreamContent = function (content) {
|
249
362
|
streamModal.find('iframe').attr('src', '');
|
250
363
|
streamModal.find('iframe').addClass('d-none');
|
251
|
-
streamModal.find('#stream-placeholder > div').text(
|
364
|
+
streamModal.find('#stream-placeholder > div').text(content);
|
252
365
|
streamModal.find('#stream-placeholder').addClass('d-flex');
|
253
|
-
|
254
|
-
stopUpdateStream();
|
255
|
-
if (!freezeTime) {
|
256
|
-
streamTimer = setTimeout(activeStream, delayStart(room.start) * 1000, roomName);
|
257
|
-
}
|
258
366
|
};
|
259
367
|
|
260
|
-
let
|
261
|
-
|
262
|
-
|
263
|
-
streamModal.find('iframe').attr('src', room.href);
|
368
|
+
let setStreamSrc = function (href) {
|
369
|
+
streamModal.find('iframe').attr('src', href);
|
264
370
|
streamModal.find('#stream-placeholder').addClass('d-none').removeClass('d-flex');
|
265
371
|
streamModal.find('iframe').removeClass('d-none');
|
372
|
+
};
|
266
373
|
|
267
|
-
|
268
|
-
|
269
|
-
|
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);
|
383
|
+
}
|
384
|
+
|
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." }}');
|
388
|
+
|
389
|
+
if (!freezeTime) {
|
390
|
+
streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomStart - streamPrepend*60) * 1000, roomName);
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
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
|
+
}
|
401
|
+
}
|
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
|
+
}
|
270
429
|
}
|
271
430
|
};
|
272
431
|
|
273
|
-
let
|
274
|
-
let
|
432
|
+
let setStreamInfo = function (roomName) {
|
433
|
+
let timeNow = time();
|
434
|
+
let talkNext = getNextTalk(roomName);
|
275
435
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
streamModal.find('#stream-placeholder').addClass('d-flex');
|
436
|
+
if (typeof streamInfoTimer !== "undefined") {
|
437
|
+
clearInterval(streamInfoTimer);
|
438
|
+
}
|
280
439
|
|
281
|
-
|
282
|
-
|
283
|
-
|
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;
|
443
|
+
updateLive();
|
444
|
+
|
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);
|
449
|
+
|
450
|
+
streamModal.find('#stream-info-talk').text(talkNext.name).attr('href', talkNext.href);
|
451
|
+
|
452
|
+
let speakerStr = '';
|
453
|
+
for (var i = 0; i < talkNext.speakers.length; i++) {
|
454
|
+
let speaker = data.speakers[talkNext.speakers[i]];
|
455
|
+
if (speaker.href == '') {
|
456
|
+
speakerStr += speaker.name +', '
|
457
|
+
}
|
458
|
+
else {
|
459
|
+
speakerStr += '<a class="text-reset" href="'+ speaker.href +'">'+ speaker.name +'</a>, ';
|
460
|
+
}
|
461
|
+
}
|
462
|
+
speakerStr = speakerStr.slice(0, -2);
|
463
|
+
streamModal.find('#stream-info-speakers').html(speakerStr);
|
464
|
+
|
465
|
+
streamModal.find('#stream-info').removeClass('d-none');
|
466
|
+
|
467
|
+
if (!freezeTime) {
|
468
|
+
streamInfoTimer = setTimeout(setStreamInfo, delayStart(talkNext.end) * 1000, roomName);
|
469
|
+
}
|
470
|
+
}
|
471
|
+
else {
|
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
|
+
}
|
284
483
|
}
|
285
484
|
};
|
286
485
|
|
287
486
|
let setStream = function (roomName) {
|
288
487
|
streamModal.find('.modal-footer .btn').removeClass('active');
|
289
|
-
streamModal.find('#stream-select').
|
488
|
+
streamModal.find('#stream-select').val(0);
|
290
489
|
|
490
|
+
// Recover room name in case of empty default
|
291
491
|
let room = getRoom(roomName);
|
292
|
-
|
492
|
+
roomName = room.name;
|
293
493
|
|
294
|
-
|
295
|
-
|
296
|
-
}
|
297
|
-
else if (tNow > room.end) {
|
298
|
-
postEndStream(roomName);
|
299
|
-
}
|
300
|
-
else {
|
301
|
-
activeStream(roomName);
|
302
|
-
}
|
494
|
+
setStreamVideo(roomName);
|
495
|
+
setStreamInfo(roomName);
|
303
496
|
|
304
497
|
streamModal.find('#stream-button' + room.id).addClass('active');
|
305
|
-
streamModal.find('#stream-select').
|
498
|
+
streamModal.find('#stream-select').val(room.id);
|
306
499
|
};
|
307
500
|
|
308
501
|
let updateStream = function () {
|
@@ -317,8 +510,11 @@
|
|
317
510
|
};
|
318
511
|
|
319
512
|
let stopUpdateStream = function () {
|
320
|
-
if (typeof
|
321
|
-
clearInterval(
|
513
|
+
if (typeof streamVideoTimer !== "undefined") {
|
514
|
+
clearInterval(streamVideoTimer);
|
515
|
+
}
|
516
|
+
if (typeof streamInfoTimer !== "undefined") {
|
517
|
+
clearInterval(streamInfoTimer);
|
322
518
|
}
|
323
519
|
};
|
324
520
|
|
@@ -331,6 +527,7 @@
|
|
331
527
|
let setupStream = function () {
|
332
528
|
streamModal = $('#stream-modal');
|
333
529
|
|
530
|
+
// configure modal opening buttons
|
334
531
|
streamModal.on('show.bs.modal', function (event) {
|
335
532
|
let button = $(event.relatedTarget);
|
336
533
|
let roomName = button.data('room');
|
@@ -340,6 +537,7 @@
|
|
340
537
|
hideModal(event);
|
341
538
|
});
|
342
539
|
|
540
|
+
// configure room selection buttons in modal
|
343
541
|
streamModal.find('.modal-footer .btn').on('click', function(event) {
|
344
542
|
event.preventDefault();
|
345
543
|
|
@@ -347,12 +545,18 @@
|
|
347
545
|
setStream(roomName);
|
348
546
|
});
|
349
547
|
|
548
|
+
// configure room selection menu in modal
|
350
549
|
streamModal.find('#stream-select').on('change', function(event) {
|
351
550
|
event.preventDefault();
|
352
551
|
|
353
552
|
let roomName = $(this).children('option:selected').text();
|
354
553
|
setStream(roomName);
|
355
554
|
});
|
555
|
+
|
556
|
+
// load data
|
557
|
+
$.getJSON('{{ site.baseurl }}/assets/js/data.json', function(json) {
|
558
|
+
data = json;
|
559
|
+
});
|
356
560
|
};
|
357
561
|
|
358
562
|
let setup = function () {
|