jekyll-theme-conference 2.5.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +101 -48
- data/_includes/js/conference-live.js +463 -151
- 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 +5 -4
- data/_includes/partials/get_day_hash.html +20 -0
- data/_includes/partials/get_day_time.html +19 -0
- data/_includes/partials/get_link.html +43 -43
- 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 +22 -6
- 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/{live-modal.html → live_modal.html} +24 -5
- data/_includes/partials/navbar.html +28 -35
- data/_includes/partials/navbar_rooms.html +21 -20
- data/_includes/partials/show_live_button.html +17 -0
- 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 +48 -6
- data/_layouts/program.html +179 -148
- data/_layouts/room.html +50 -43
- data/_layouts/speaker.html +46 -42
- data/_layouts/talk-overview.html +83 -53
- data/_layouts/talk.html +43 -42
- data/_sass/conference.scss +29 -12
- data/assets/icons/live.svg +33 -57
- data/assets/js/data.json +3 -0
- data/assets/js/main.js +0 -6
- metadata +13 -7
- 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/_includes/partials/live_button.html +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4319ed637aa4734e12cd0d9eef3a807f314e8a9e4c0ff3a79b8781e8e00ebb9
|
4
|
+
data.tar.gz: d0c52c4aa84e87fb5c9892b6509db3de79a640e2e3ad005359febda835c412be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 849e5facbe864d96c63fcdcd218ee4a4a4d8e63a9787aca58c10b1fafdf20704ce3d529caa519940ae65bd3a561ffc6127f2da243a823e676604b538e2f24c35
|
7
|
+
data.tar.gz: 5bca6a3b8624e8e8f04797292cc969dd2f995fb74ffedd178dee2d59b0410cd93d1dd5479d8c64cdc99728ca7160e05c31e9537a93866093ed88bd65142e0a03
|
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
|
@@ -89,6 +83,10 @@ The different talks, speakers and rooms are stored as a collection of files. Eac
|
|
89
83
|
|
90
84
|
The actual schedule defining when and in which room a talk takes place is stored as a [YAML data file](https://jekyllrb.com/docs/datafiles/) under `_data/program.yml`. For further details about it see below in the section _Content_.
|
91
85
|
|
86
|
+
:warning: Please note that the generated website can be quite large containing many unnecessary whitespaces. It is recommended to minimize the generated output files before uploading them to a server (e.g. with [minify](https://github.com/tdewolff/minify)).
|
87
|
+
|
88
|
+
### Jump Start
|
89
|
+
|
92
90
|
In order to be up and running simply use the default content of this repository as an initial base for your new website. After having setup a new Jekyll website copy the following files and folders into the website's folder:
|
93
91
|
|
94
92
|
- `_config.example.yml` -> `_config.yml`
|
@@ -102,11 +100,43 @@ In order to be up and running simply use the default content of this repository
|
|
102
100
|
- `speakers/`
|
103
101
|
- `talks/`
|
104
102
|
|
105
|
-
|
103
|
+
### Automatic Import
|
104
|
+
|
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
|
106
108
|
|
107
|
-
|
109
|
+
2. Create a virtual environment and activate it
|
108
110
|
|
109
|
-
|
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
|
+
|
128
|
+
|
129
|
+
### Automatic Build
|
130
|
+
|
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:
|
132
|
+
|
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.
|
136
|
+
|
137
|
+
To get started, simply copy the desired workflow file to your repository and adapt it to your needs:
|
138
|
+
|
139
|
+
- `_tools/build.yml` -> `.github/workflows/build.yml`
|
110
140
|
|
111
141
|
|
112
142
|
## Configuration
|
@@ -124,6 +154,8 @@ conference:
|
|
124
154
|
show_errors: false
|
125
155
|
```
|
126
156
|
|
157
|
+
:warning: Please be sure to disable this parameter for your production system.
|
158
|
+
|
127
159
|
### Collection URLs
|
128
160
|
|
129
161
|
The three required collections containing the files for the talks, speakers and rooms have to be specified in the `_config.yml` file. The first block declares them and sets the URL under which they will later be accessed. The second block defines the default layout for each of the collection.
|
@@ -183,11 +215,6 @@ conference:
|
|
183
215
|
|
184
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.
|
185
217
|
|
186
|
-
Additionally, a navigation bar link can also have the following properties:
|
187
|
-
|
188
|
-
- `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
|
189
|
-
- `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).
|
190
|
-
|
191
218
|
Example:
|
192
219
|
|
193
220
|
```yaml
|
@@ -294,21 +321,23 @@ In order to help users navigating the program during the congress, a _Live_ indi
|
|
294
321
|
|
295
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).
|
296
323
|
|
297
|
-
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
|
298
325
|
|
299
|
-
-
|
300
|
-
-
|
301
|
-
|
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`),
|
302
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`).
|
303
332
|
|
304
333
|
```yaml
|
305
334
|
conference:
|
306
335
|
live:
|
307
|
-
|
308
|
-
timezone: GMT+1
|
336
|
+
time_stop: 240 # in minutes
|
309
337
|
streaming:
|
310
|
-
|
311
|
-
|
338
|
+
time_pause: 60 # in minutes
|
339
|
+
time_prepend: 5 # in minutes
|
340
|
+
time_extend: 5 # in minutes
|
312
341
|
demo: false
|
313
342
|
```
|
314
343
|
|
@@ -430,33 +459,52 @@ The actual schedule defining when and in which room a talk takes place is stored
|
|
430
459
|
|
431
460
|
### Schedule / Program
|
432
461
|
|
433
|
-
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
|
434
465
|
|
435
|
-
- `
|
436
|
-
-
|
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`.
|
437
470
|
|
438
|
-
|
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
|
439
479
|
|
440
480
|
- a `name` (must correspond to one of the talk identifier),
|
441
481
|
- a starting time `time_start` given as `H:M` ([`strftime`](http://www.strfti.me) formated), and
|
442
482
|
- an end time `time_end`.
|
443
483
|
|
444
|
-
The
|
484
|
+
The list of talks should (manually) be ordered by time, i.e. the first occurring talk should be listed first.
|
445
485
|
|
446
486
|
Example:
|
447
487
|
|
448
488
|
```yaml
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
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'
|
460
508
|
```
|
461
509
|
|
462
510
|
### Talks
|
@@ -501,6 +549,11 @@ Links are used at different location throughout the theme: They can either be us
|
|
501
549
|
+ pointing to a file uploaded to the `/documents` folder (for talks `/documents/slides`, for speakers `/documents/bio`): `file:`
|
502
550
|
+ pointing to an external video: `video:`
|
503
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. 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
|
+
|
504
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.
|
505
558
|
|
506
559
|
Example:
|
@@ -1,11 +1,21 @@
|
|
1
1
|
window.conference.live = (function() {
|
2
|
-
{
|
3
|
-
{%-
|
4
|
-
{%- assign
|
5
|
-
{%- include partials/get_timestamp.html -%}
|
2
|
+
{% assign d = site.data.program.days | first -%}
|
3
|
+
{%- include partials/get_day_time.html -%}
|
4
|
+
{%- assign t = day_start_talk -%}
|
6
5
|
|
7
|
-
|
8
|
-
|
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 }};
|
18
|
+
let confDur = confEnd - confStart;
|
9
19
|
|
10
20
|
let freezeTime = false;
|
11
21
|
let timeFrozen = 0;
|
@@ -15,250 +25,509 @@ window.conference.live = (function() {
|
|
15
25
|
let durDemo = 5*60; // in seconds
|
16
26
|
let durPause = 10; // in seconds
|
17
27
|
|
28
|
+
let demoStart = confStart - confDur/durDemo*durPause;
|
29
|
+
let demoEnd = confEnd + confDur/durDemo*durPause;
|
30
|
+
|
18
31
|
let liveTimer;
|
19
|
-
let
|
32
|
+
let streamVideoTimer;
|
33
|
+
let streamInfoTimer;
|
34
|
+
|
35
|
+
let mod = function (n, m) {
|
36
|
+
return ((n % m) + m) % m;
|
37
|
+
};
|
20
38
|
|
21
|
-
let
|
22
|
-
// Return UNIX timestamp in seconds
|
39
|
+
let timeNow = function () {
|
23
40
|
return Math.floor(Date.now() / 1000);
|
24
41
|
};
|
25
42
|
|
26
|
-
let
|
27
|
-
|
28
|
-
|
29
|
-
|
43
|
+
let timeCont = function () {
|
44
|
+
return timeNow() - timeOffset;
|
45
|
+
};
|
46
|
+
|
47
|
+
let timeCycle = function () {
|
48
|
+
let actTime = timeNow();
|
49
|
+
let relTime = mod(actTime, durDemo + 2*durPause) / durDemo;
|
50
|
+
let cycleTime = mod((demoEnd - demoStart) * relTime - timeOffset, (demoEnd - demoStart)) + demoStart;
|
30
51
|
return cycleTime;
|
31
52
|
};
|
32
53
|
|
33
|
-
let
|
54
|
+
let time = function () {
|
34
55
|
if (freezeTime) {
|
35
56
|
return timeFrozen;
|
36
57
|
}
|
37
58
|
else if (demo) {
|
38
|
-
return
|
59
|
+
return timeCycle();
|
39
60
|
}
|
40
61
|
else {
|
41
|
-
return
|
62
|
+
return timeCont();
|
42
63
|
}
|
43
64
|
};
|
44
65
|
|
45
66
|
let pauseTime = function () {
|
46
|
-
|
47
|
-
|
67
|
+
if (!freezeTime) {
|
68
|
+
timeFrozen = time();
|
69
|
+
freezeTime = true;
|
70
|
+
|
71
|
+
stopUpdate();
|
72
|
+
}
|
48
73
|
};
|
49
74
|
|
50
75
|
let continueTime = function () {
|
51
|
-
if (
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
timeOffset = timeNow() - timeFrozen;
|
76
|
+
if (freezeTime) {
|
77
|
+
freezeTime = false;
|
78
|
+
timeOffset += time() - timeFrozen;
|
79
|
+
startUpdate();
|
56
80
|
}
|
57
|
-
freezeTime = false;
|
58
81
|
};
|
59
82
|
|
60
83
|
let resetTime = function (timeStr) {
|
61
|
-
continueTime();
|
62
84
|
timeOffset = 0;
|
85
|
+
freezeTime = false;
|
86
|
+
|
87
|
+
startUpdate();
|
63
88
|
};
|
64
89
|
|
65
|
-
let setTime = function (
|
90
|
+
let setTime = function (newTime, newDay=1) {
|
66
91
|
pauseTime();
|
67
92
|
|
68
|
-
let
|
69
|
-
|
70
|
-
|
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);
|
106
|
+
newTime = newTime.split(':');
|
107
|
+
d.setHours(newTime[0], newTime[1]);
|
71
108
|
|
72
109
|
timeFrozen = Math.floor(d.getTime() / 1000);
|
110
|
+
|
111
|
+
update();
|
73
112
|
};
|
74
113
|
|
75
|
-
let getTime = function () {
|
76
|
-
let d = new Date(
|
114
|
+
let getTime = function (tConvert=time()) {
|
115
|
+
let d = new Date(tConvert * 1000);
|
116
|
+
let dStr = d.toISOString().slice(0,10);
|
77
117
|
let h = d.getHours();
|
78
118
|
let m = d.getMinutes();
|
79
119
|
|
80
|
-
return
|
120
|
+
return dStr +" "+ h +":"+ (m < 10 ? "0" : "") + m;
|
81
121
|
};
|
82
122
|
|
83
|
-
let
|
84
|
-
|
85
|
-
|
86
|
-
// Start when conference start (-60s)
|
87
|
-
return confStart - 60 - tNow;
|
123
|
+
let timeUnit = function () {
|
124
|
+
if (demo) {
|
125
|
+
return 0.1;
|
88
126
|
}
|
89
127
|
else {
|
90
|
-
|
91
|
-
return (60 - (tNow % 60));
|
128
|
+
return 60;
|
92
129
|
}
|
93
130
|
};
|
94
131
|
|
95
|
-
let
|
96
|
-
let tNow =
|
132
|
+
let delayStart = function (startTime) {
|
133
|
+
let tNow = time();
|
134
|
+
let tUnit = timeUnit();
|
135
|
+
|
136
|
+
if (demo) {
|
137
|
+
// Convert virtual duration to real duration
|
138
|
+
return mod(startTime - tNow, demoEnd - demoStart) / (demoEnd - demoStart) * (durDemo + 2*durPause);
|
139
|
+
}
|
140
|
+
else {
|
141
|
+
if (startTime > tNow) {
|
142
|
+
return startTime - tNow;
|
143
|
+
}
|
144
|
+
else {
|
145
|
+
// Start on the unit
|
146
|
+
return (tUnit - (tNow % tUnit));
|
147
|
+
}
|
148
|
+
}
|
149
|
+
};
|
150
|
+
|
151
|
+
let toggleDemo = function () {
|
152
|
+
demo = !demo;
|
153
|
+
resetTime();
|
154
|
+
};
|
155
|
+
|
156
|
+
let demoOn = function () {
|
157
|
+
return demo;
|
158
|
+
};
|
159
|
+
|
160
|
+
let updateLive = function () {
|
161
|
+
let tNow = time();
|
97
162
|
let liveShow = document.getElementsByClassName('live-show');
|
98
163
|
let liveHide = document.getElementsByClassName('live-hide');
|
164
|
+
let liveTime = document.getElementsByClassName('live-time');
|
165
|
+
let livePast = document.getElementsByClassName('live-past');
|
99
166
|
|
100
167
|
for (let i = 0; i < liveShow.length; i++) {
|
101
|
-
let
|
102
|
-
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
|
+
}
|
103
183
|
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
+
}
|
107
200
|
}
|
108
|
-
|
109
|
-
|
110
|
-
|
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;
|
209
|
+
|
210
|
+
let tStr;
|
211
|
+
if (tRel >= -60 && tRel < 0) {
|
212
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.soon | default: "soon" }}';
|
213
|
+
}
|
214
|
+
else if (tRel >= 0 && tRel < 60) {
|
215
|
+
tStr = '{{ site.data.lang[site.conference.lang].live.time.now | default: "now" }}';
|
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
|
+
}
|
111
257
|
}
|
258
|
+
|
259
|
+
liveTime[i].innerHTML = tStr;
|
112
260
|
}
|
113
261
|
|
114
|
-
for (let i = 0; i <
|
115
|
-
let
|
116
|
-
|
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;
|
117
268
|
|
118
|
-
if (
|
119
|
-
//
|
120
|
-
if (!
|
121
|
-
|
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');
|
122
273
|
}
|
123
274
|
}
|
124
275
|
else {
|
125
|
-
// Show otherwise
|
126
|
-
|
276
|
+
// Show normal otherwise
|
277
|
+
livePast[i].classList.remove('text-secondary');
|
127
278
|
}
|
128
279
|
}
|
129
280
|
|
130
|
-
if (
|
281
|
+
if (tNow > confEnd && !demo) {
|
131
282
|
// Cancel timer after program is over
|
132
|
-
|
283
|
+
stopUpdateLive();
|
133
284
|
}
|
134
285
|
};
|
135
286
|
|
136
|
-
let
|
137
|
-
|
138
|
-
|
139
|
-
}
|
140
|
-
updateLiveButtons();
|
287
|
+
let startUpdateLive = function () {
|
288
|
+
stopUpdateLive();
|
289
|
+
updateLive();
|
141
290
|
|
142
291
|
if (demo) {
|
143
|
-
|
144
|
-
liveTimer = setInterval(
|
292
|
+
// Immediate start required since delayStart would wait for next wrap around
|
293
|
+
liveTimer = setInterval(updateLive, timeUnit() * 1000);
|
145
294
|
}
|
146
295
|
else {
|
147
|
-
liveTimerCorr = 1;
|
148
296
|
setTimeout(function() {
|
149
|
-
liveTimer = setInterval(
|
150
|
-
|
151
|
-
},
|
297
|
+
liveTimer = setInterval(updateLive, timeUnit() * 1000);
|
298
|
+
updateLive();
|
299
|
+
}, delayStart(confStart) * 1000);
|
152
300
|
}
|
153
301
|
};
|
154
302
|
|
155
|
-
let
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
};
|
160
|
-
|
161
|
-
let demoOn = function() {
|
162
|
-
return demo;
|
303
|
+
let stopUpdateLive = function () {
|
304
|
+
if (typeof liveTimer !== "undefined") {
|
305
|
+
clearInterval(liveTimer);
|
306
|
+
}
|
163
307
|
};
|
164
308
|
|
165
309
|
{% if site.conference.live.streaming -%}
|
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
|
313
|
+
|
314
|
+
let streamModal;
|
315
|
+
let data;
|
166
316
|
|
167
|
-
let
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
{%- assign time_start = talk_start -%}
|
175
|
-
{%- assign time_end = talk_end -%}
|
176
|
-
{%- include partials/get_timestamp.html -%}
|
177
|
-
|
178
|
-
{%- assign offset_start = site.conference.live.streaming.start_early | default: 0 -%}
|
179
|
-
{%- assign room_ts_start = offset_start | times: -60 | plus: timestamp_start -%}
|
180
|
-
|
181
|
-
{%- assign t = r.talks | last -%}
|
182
|
-
{%- include partials/get_talk_time.html -%}
|
183
|
-
{%- assign time_start = talk_start -%}
|
184
|
-
{%- assign time_end = talk_end -%}
|
185
|
-
{%- include partials/get_timestamp.html -%}
|
186
|
-
|
187
|
-
{%- assign offset_end = site.conference.live.streaming.end_late | default: 0 -%}
|
188
|
-
{%- assign room_ts_end = offset_end | times: 60 | plus: timestamp_end -%}
|
189
|
-
|
190
|
-
"{{ room.name }}": {
|
191
|
-
"id": {{ forloop.index }},
|
192
|
-
"href": "{{ room.live }}",
|
193
|
-
"start": {{ room_ts_start }},
|
194
|
-
"end": {{ room_ts_end }}
|
195
|
-
},
|
196
|
-
{%- endif -%}
|
197
|
-
{%- endfor -%}
|
317
|
+
let getRoom = function (roomName) {
|
318
|
+
if (roomName in data.rooms) {
|
319
|
+
return data.rooms[roomName];
|
320
|
+
}
|
321
|
+
else {
|
322
|
+
return data.rooms[Object.keys(data.rooms)[0]];
|
323
|
+
}
|
198
324
|
};
|
199
325
|
|
200
|
-
let
|
201
|
-
|
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
|
+
};
|
202
360
|
|
203
|
-
let
|
361
|
+
let setStreamContent = function (content) {
|
204
362
|
streamModal.find('iframe').attr('src', '');
|
205
363
|
streamModal.find('iframe').addClass('d-none');
|
206
|
-
streamModal.find('#stream-placeholder > div').text(
|
364
|
+
streamModal.find('#stream-placeholder > div').text(content);
|
207
365
|
streamModal.find('#stream-placeholder').addClass('d-flex');
|
366
|
+
};
|
208
367
|
|
209
|
-
|
210
|
-
clearTimeout(streamTimer);
|
211
|
-
}
|
212
|
-
streamTimer = setTimeout(activeStream, (startTime - timeNow())/liveTimerCorr*1000, href, endTime);
|
213
|
-
}
|
214
|
-
|
215
|
-
let activeStream = function(href, endTime) {
|
368
|
+
let setStreamSrc = function (href) {
|
216
369
|
streamModal.find('iframe').attr('src', href);
|
217
370
|
streamModal.find('#stream-placeholder').addClass('d-none').removeClass('d-flex');
|
218
371
|
streamModal.find('iframe').removeClass('d-none');
|
372
|
+
};
|
219
373
|
|
220
|
-
|
221
|
-
|
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);
|
222
383
|
}
|
223
|
-
streamTimer = setTimeout(postEndStream, (endTime - timeNow())/liveTimerCorr*1000);
|
224
|
-
}
|
225
384
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
streamModal.find('#stream-placeholder > div').text('{{ site.data.lang[site.conference.lang].live.post_stream | default: "Live stream has ended." }}');
|
230
|
-
streamModal.find('#stream-placeholder').addClass('d-flex');
|
231
|
-
}
|
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." }}');
|
232
388
|
|
233
|
-
|
234
|
-
|
235
|
-
|
389
|
+
if (!freezeTime) {
|
390
|
+
streamVideoTimer = setTimeout(setStreamVideo, delayStart(roomStart - streamPrepend*60) * 1000, roomName);
|
391
|
+
}
|
236
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
|
237
404
|
else {
|
238
|
-
|
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
|
+
}
|
239
429
|
}
|
430
|
+
};
|
240
431
|
|
241
|
-
|
242
|
-
|
243
|
-
|
432
|
+
let setStreamInfo = function (roomName) {
|
433
|
+
let timeNow = time();
|
434
|
+
let talkNext = getNextTalk(roomName);
|
435
|
+
|
436
|
+
if (typeof streamInfoTimer !== "undefined") {
|
437
|
+
clearInterval(streamInfoTimer);
|
244
438
|
}
|
245
|
-
|
246
|
-
|
439
|
+
|
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
|
+
}
|
247
470
|
}
|
248
471
|
else {
|
249
|
-
|
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
|
+
}
|
250
483
|
}
|
484
|
+
};
|
485
|
+
|
486
|
+
let setStream = function (roomName) {
|
487
|
+
streamModal.find('.modal-footer .btn').removeClass('active');
|
488
|
+
streamModal.find('#stream-select').selectedIndex = -1;
|
489
|
+
|
490
|
+
// Recover room name in case of empty default
|
491
|
+
let room = getRoom(roomName);
|
492
|
+
roomName = room.name;
|
493
|
+
|
494
|
+
setStreamVideo(roomName);
|
495
|
+
setStreamInfo(roomName);
|
496
|
+
|
251
497
|
streamModal.find('#stream-button' + room.id).addClass('active');
|
498
|
+
streamModal.find('#stream-select').selectedIndex = room.id;
|
499
|
+
};
|
500
|
+
|
501
|
+
let updateStream = function () {
|
502
|
+
if (streamModal.hasClass('show')) {
|
503
|
+
let activeButton = streamModal.find('.modal-footer .btn.active');
|
504
|
+
let roomName = activeButton.data('room');
|
505
|
+
|
506
|
+
if (typeof roomName !== "undefined") {
|
507
|
+
setStream(roomName);
|
508
|
+
}
|
509
|
+
}
|
510
|
+
};
|
511
|
+
|
512
|
+
let stopUpdateStream = function () {
|
513
|
+
if (typeof streamVideoTimer !== "undefined") {
|
514
|
+
clearInterval(streamVideoTimer);
|
515
|
+
}
|
516
|
+
if (typeof streamInfoTimer !== "undefined") {
|
517
|
+
clearInterval(streamInfoTimer);
|
518
|
+
}
|
252
519
|
};
|
253
520
|
|
254
521
|
let hideModal = function (event) {
|
255
522
|
streamModal.find('iframe').attr('src', '');
|
256
523
|
streamModal.find('.modal-footer .btn').removeClass('active');
|
524
|
+
streamModal.find('#stream-select').selectedIndex = -1;
|
257
525
|
};
|
258
526
|
|
259
|
-
let
|
527
|
+
let setupStream = function () {
|
260
528
|
streamModal = $('#stream-modal');
|
261
529
|
|
530
|
+
// configure modal opening buttons
|
262
531
|
streamModal.on('show.bs.modal', function (event) {
|
263
532
|
let button = $(event.relatedTarget);
|
264
533
|
let roomName = button.data('room');
|
@@ -268,27 +537,70 @@ window.conference.live = (function() {
|
|
268
537
|
hideModal(event);
|
269
538
|
});
|
270
539
|
|
540
|
+
// configure room selection buttons in modal
|
271
541
|
streamModal.find('.modal-footer .btn').on('click', function(event) {
|
272
542
|
event.preventDefault();
|
273
543
|
|
274
|
-
let roomName = $(this).data('room')
|
544
|
+
let roomName = $(this).data('room');
|
545
|
+
setStream(roomName);
|
546
|
+
});
|
547
|
+
|
548
|
+
// configure room selection menu in modal
|
549
|
+
streamModal.find('#stream-select').on('change', function(event) {
|
550
|
+
event.preventDefault();
|
551
|
+
|
552
|
+
let roomName = $(this).children('option:selected').text();
|
275
553
|
setStream(roomName);
|
276
554
|
});
|
555
|
+
|
556
|
+
// load data
|
557
|
+
$.getJSON('{{ site.baseurl }}/assets/js/data.json', function(json) {
|
558
|
+
data = json;
|
559
|
+
});
|
560
|
+
};
|
561
|
+
|
562
|
+
let setup = function () {
|
563
|
+
startUpdateLive();
|
564
|
+
setupStream();
|
565
|
+
};
|
566
|
+
|
567
|
+
let update = function () {
|
568
|
+
updateLive();
|
569
|
+
updateStream();
|
570
|
+
};
|
571
|
+
|
572
|
+
let startUpdate = function () {
|
573
|
+
startUpdateLive();
|
574
|
+
updateStream();
|
575
|
+
};
|
576
|
+
|
577
|
+
let stopUpdate = function () {
|
578
|
+
stopUpdateLive();
|
579
|
+
stopUpdateStream();
|
277
580
|
};
|
278
581
|
|
279
582
|
{%- else -%}
|
280
583
|
|
281
|
-
let
|
584
|
+
let setup = function () {
|
585
|
+
startUpdateLive();
|
586
|
+
};
|
282
587
|
|
283
|
-
|
588
|
+
let update = function () {
|
589
|
+
updateLive();
|
590
|
+
};
|
284
591
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
592
|
+
let startUpdate = function () {
|
593
|
+
startUpdateLive();
|
594
|
+
};
|
595
|
+
|
596
|
+
let stopUpdate = function () {
|
597
|
+
stopUpdateLive();
|
598
|
+
};
|
599
|
+
|
600
|
+
{%- endif %}
|
289
601
|
|
290
602
|
return {
|
291
|
-
init:
|
603
|
+
init: setup,
|
292
604
|
|
293
605
|
pauseTime: pauseTime,
|
294
606
|
continueTime: continueTime,
|
@@ -297,7 +609,7 @@ window.conference.live = (function() {
|
|
297
609
|
getTime: getTime,
|
298
610
|
|
299
611
|
toggleDemo: toggleDemo,
|
300
|
-
|
612
|
+
demo: demoOn,
|
301
613
|
durDemo: durDemo,
|
302
614
|
durPause: durPause
|
303
615
|
};
|