improve linebreaks in TS Meetup talk
[slides.git] / tsmeetup201909 / slides.js
1 /******************************
2  * JavaScript for talk slides *
3  *      by Robert Kaiser      *
4  *      <kairo@kairo.at>      *
5  *     (for FOSDEM 2011)      *
6  ******************************/
7
8 var slides = {};
9 var articleNodes;
10 var currentSlide;
11 var currentIdx;
12 var defaultIdx = 1; // set to slide index to show by default
13 var firstIdx = 2; // set no value if to use first available
14 var lastIdx; // set no value if to use last available
15
16 var pageTitle, headerText, subHeaderText;
17 var navPrev, navNext, navPrevNolink, navNextNolink;
18
19 // Slide timer - color variation of headerText
20 // Time per slide is total presentation length divided by number of slides
21 // except start and end slide.
22 var presLengthSeconds = 5 * 60;
23 var slideStart, timerMSec;
24
25 // Called when the document has been loaded.
26 window.onload = function() {
27   pageTitle = document.getElementsByTagName("title")[0];
28   headerText = document.getElementById("header-text");
29   subHeaderText = document.getElementById("subheader-text");
30   navPrev = document.getElementById("nav-prev");
31   navNext = document.getElementById("nav-next");
32   navPrevNolink = document.getElementById("nav-prev-nolink");
33   navNextNolink = document.getElementById("nav-next-nolink");
34   articleNodes = document.getElementsByTagName("article");
35
36   if (!firstIdx)
37     firstIdx = 0;
38   if (!lastIdx)
39     lastIdx = articleNodes.length - 1;
40   var slideSeconds = presLengthSeconds / (lastIdx - firstIdx);
41   timerMSec = 1000 * (slideSeconds / 3);
42
43   // Get a list of all slides (articles).
44   subHeaderText.textContent = articleNodes.length + " slides...";
45   for (var i = 0; i < articleNodes.length; ++i) {
46     subHeaderText.textContent = "Indexing slide " + i + " / " + articleNodes.length;
47     if (!articleNodes[i].id)
48       articleNodes[i].id = "slide_" + i;
49
50     slides[articleNodes[i].id] = {
51         "idx": i,
52         "name": articleNodes[i].id,
53         "title": articleNodes[i].dataset.title ? articleNodes[i].dataset.title : articleNodes[i].id,
54         "obj": articleNodes[i],
55         "timeSeconds": articleNodes[i].dataset.seconds ? articleNodes[i].dataset.seconds : slideSeconds,
56     };
57
58     if (location.hash.length &&
59         (location.hash == "#" + articleNodes[i].id || location.hash == "#" + i)) {
60       articleNodes[i].setAttribute("aria-selected", "true");
61       currentSlide = slides[articleNodes[i].id];
62       currentIdx = i;
63     }
64   }
65
66   if (!currentSlide) {
67     currentIdx = defaultIdx;
68     currentSlide = slides[articleNodes[currentIdx].id];
69     currentSlide.obj.setAttribute("aria-selected", "true");
70     location.hash = "#" + currentSlide.name;
71   }
72   updateDisplay();
73   document.getElementById("hidesdesc").onclick = function(event) {
74     document.getElementById("slidesdesc").classList.toggle("hidden");
75   }
76 }
77
78 // Called when the hash part of the location changes.
79 function locationHashChanged() {
80   if (location.hash.length > 1) {
81     var hashtag = location.hash.substring(1);
82     // If not a number, treat as ID
83     if (isNaN(hashtag) && slides[hashtag]) {
84       currentSlide.obj.removeAttribute("aria-selected");
85       currentSlide = slides[hashtag];
86       currentIdx = currentSlide.idx;
87       currentSlide.obj.setAttribute("aria-selected", "true");
88       updateDisplay();
89     }
90     else if (articleNodes[hashtag]) {
91       currentSlide.obj.removeAttribute("aria-selected");
92       currentIdx = hashtag;
93       currentSlide = slides[articleNodes[currentIdx].id];
94       currentSlide.obj.setAttribute("aria-selected", "true");
95       updateDisplay();
96     }
97   }
98 }
99 window.onhashchange = locationHashChanged;
100
101 // Update the display after we updated what slide is shown.
102 function updateDisplay() {
103   if (currentIdx >= firstIdx && currentIdx <= lastIdx &&
104       currentSlide.name != "toc")
105     subHeaderText.textContent = (currentIdx - firstIdx + 1) + "/" +
106                                 (lastIdx - firstIdx + 1) + " - " +
107                                 currentSlide.title;
108   else
109     subHeaderText.textContent = currentSlide.title;
110   pageTitle.textContent = headerText.textContent + ": " + currentSlide.title;
111   if (currentIdx > firstIdx && currentSlide.name != "toc") {
112     navPrev.hidden = false;
113     navPrev.href = "#" + articleNodes[currentIdx - 1].id;
114     navPrevNolink.hidden = true;
115   }
116   else {
117     navPrev.hidden = true;
118     navPrevNolink.hidden = false;
119   }
120   if (currentIdx < lastIdx && currentSlide.name != "toc") {
121     navNext.hidden = false;
122     navNext.href = "#" + articleNodes[currentIdx + 1].id;
123     navNextNolink.hidden = true;
124   }
125   else {
126     navNext.hidden = true;
127     navNextNolink.hidden = false;
128   }
129   headerText.className = "";
130   slideStart = new Date();
131   timerMSec = 1000 * (currentSlide.timeSeconds / 3);
132   if (currentSlide.name == "toc")
133     createTOC();
134   else
135     setTimeout(timerFired, timerMSec);
136 }
137
138 // Create TOC list.
139 function createTOC() {
140  var list = document.getElementById("toc-list");
141  if (!list.getElementsByTagName("li").length) {
142    for (var slide in slides) {
143      if (slide != "toc") {
144        var item = document.createElement("li");
145        var link = document.createElement("a");
146        var slideHeaders = slides[slide].obj.getElementsByTagName("h1");
147        if (slideHeaders.length)
148          link.textContent = slideHeaders[0].textContent;
149        else
150          link.textContent = slides[slide].title;
151        link.href = "#" + slides[slide].name;
152        item.appendChild(link);
153        list.appendChild(item);
154      }
155    }
156  }
157 }
158
159 // Do timed color variation on slides.
160 function timerFired() {
161   var slideCurrent = new Date();
162   var secondsDiff = Math.round((slideCurrent.getTime() - slideStart.getTime()) / 1000);
163   if (secondsDiff >= currentSlide.timeSeconds) {
164     headerText.className = "overtime";
165   }
166   else if (secondsDiff >= Math.round(2 * currentSlide.timeSeconds / 3)) {
167     headerText.className = "ontime";
168     setTimeout(timerFired, timerMSec);
169   }
170   else if (secondsDiff >= Math.round(currentSlide.timeSeconds / 3)) {
171     headerText.className = "neartime";
172     setTimeout(timerFired, timerMSec);
173   }
174   else {
175     // We should never come here, but if we do, go into a 100ms loop until we get over the upcoming step.
176     setTimeout(timerFired, 100);
177   }
178 }
179
180 // Keyboard/click nav functionality, mostly inherited from FOSDEM 2007.
181 (function() {
182   function go(where) {
183     where = where || "next";
184     var navElem = document.getElementById("nav-" + where);
185     if (!navElem.hidden)
186       window.location.href = navElem.href;
187   }
188
189   function handleClick(e) {
190     e = e || event;
191     var target = (window.event) ? e.srcElement : e.target;
192     if (e.which == 1 && target.nodeName != "A" && target.nodeName != "VIDEO" && !target.classList.contains("noadvance"))
193       go("next");
194   }
195
196   function handleKeyPress(e) {
197     e = e || event;
198     switch (e.key) {
199       // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value
200       case "ArrowLeft":
201       case "ArrowDown":
202       case "Left": // non-standard, old browsers
203       case "Down": // non-standard, old browsers
204       case "PageDown":
205       case "P":
206       case "p":
207       case "H": //8bitdo Zero "X"
208       case "h": //8bitdo Zero "X"
209         go("prev"); break;
210       case "ArrowRight":
211       case "ArrowUp":
212       case "Right": // non-standard, old browsers
213       case "Up": // non-standard, old browsers
214       case "PageUp":
215       case "N":
216       case "n":
217       case "J": //8bitdo Zero "B"
218       case "j": //8bitdo Zero "B"
219         go("next"); break;
220       case "Home":
221       case "I": //8bitdo Zero "Y"
222       case "i": //8bitdo Zero "Y"
223         go("start"); break;
224       case "End":
225       case "G": //8bitdo Zero "A"
226       case "g": //8bitdo Zero "A"
227         go("toc"); break;
228     }
229   }
230
231   window.onclick = handleClick;
232   window.onkeydown = handleKeyPress;
233 })();