window.addEventListener('load', init);
window.addEventListener('resize', reactToResizing);
window.addEventListener('hashchange', resumeOrGoToStart);
let slides;
let currentSlide;
let topicList;
let topicLinks;
let topicListSearch;
const activeTopicLinkClass = 'active-topic-link';
const notMatchingSearchTermsClass = 'not-matching-search-terms';
const topicListSearchClass = 'topic-list-search';
function init() {
slides = Array.from(document.querySelectorAll('article'));
for (let i = 0; i < slides.length; i++) {
slides[i].id = `slide${i}`;
}
topicList = createTopicList();
resumeOrGoToStart();
document.addEventListener('keydown', onKeyPressed);
}
function resumeOrGoToStart() {
let urlParts = window.location.href.split('#');
if (urlParts.length > 1) {
goToSlide(parseInt(urlParts[1].replace('slide', '')))
} else {
goToSlide(0)
}
}
function goToSlide(index) {
if (index >= 0 && index < slides.length) {
currentSlide = index;
window.location.href = window.location.href.split('#')[0] + `#slide${index}`;
let oldActiveTopicLinks = topicList.querySelectorAll(`.${activeTopicLinkClass}`);
[...oldActiveTopicLinks].forEach(link => {
link.classList.remove(activeTopicLinkClass);
});
let newActiveTopicLinks = topicList.querySelectorAll(`[data-slide*=";slide${index};"]`);
[...newActiveTopicLinks].forEach(link => {
link.classList.add(activeTopicLinkClass);
})
}
}
function goToPreviousSlide() {
if (!showPreviousFragment()) {
goToSlide(currentSlide - 1);
}
}
function goToNextSlide() {
if (!showNextFragment()) {
goToSlide(currentSlide + 1);
}
}
function goToFirstSlide() {
goToSlide(0);
}
function goToLastSlide() {
goToSlide(slides.length - 1);
}
function onKeyPressed(event) {
switch (event.keyCode) {
case 34: // page down
case 40: // arrow down
case 39: // arrow right
goToNextSlide();
break;
case 33: // page up
case 38: // arrow up
case 37: // arrow left
goToPreviousSlide();
break;
case 36: // pos1
goToFirstSlide();
break;
case 35: // end
goToLastSlide();
break;
case 17: // ctrl
toggleTopicList();
break;
case 27: // esc
hideTopicList();
break;
}
}
function showNextFragment() {
let fragments = [...slides[currentSlide].querySelectorAll('.fragment')]
let visible = [...slides[currentSlide].querySelectorAll('.fragment.visible')]
if (fragments.length == visible.length) {
return false;
} else {
fragments[visible.length].classList.add('visible')
return true;
}
}
function showPreviousFragment() {
let visible = [...slides[currentSlide].querySelectorAll('.fragment.visible')]
if (visible.length == 0) {
return false;
} else {
visible[visible.length - 1].classList.remove('visible')
return true;
}
}
/**
* Store for the headline nodes of each chapter.
*/
class HeadlineStore {
constructor() {
this.topLevelChildren = [];
}
/**
* Add a new root node, i.e. the headline of a new chapter.
* @param {Node} node the level-1 headline node
*/
add(node) {
this.topLevelChildren.push(node);
}
/**
* Generate a HTML list of all headline nodes stored in this structure.
*/
getHTML() {
let resultHTML = '
'
}
}
/**
* Data structure storing all headlines for the topic list.
* Each node knows its parents and children.
*/
class Node {
constructor(title, level, father, slide) {
this.children = [];
this.slidesWithSameTitle = [];
this.title = title;
this.level = level;
this.father = father;
this.slide = slide;
}
/**
* Return the child that was added last among all childen.
*/
getLastChild() {
return this.children[this.children.length - 1];
}
/**
* Add a child node to this headline if the title of the new child node is
* different from the title of the node that was added last.
* This way, headlines are not added twice if they appear consecutively.
* @param {Node} node a subtitle node
* @return true, if the node was added, false otherwise
*/
addChildNode(node) {
if (!this.lastChildHasTitle(node.title)) {
this.children.push(node);
return true;
} else {
return false;
}
}
/**
* Each headline can have more than one slide related to it.
* Consecutive headlines appear only ones in a dictionary, but they are still
* related to all slides. If there are three slides with title »Example« in a
* row, the node will have slide1 as its child and slide2 and slide3 as
* entries to the slidesWithSameTitle array.
* The goal of this is to ultimately highlight the node in the topic list not
* only when slide1 is active, but also for slide2 and slide3.
* @param {Integer} slide the ID of a related slide
*/
addSlideWithSameTitle(slide) {
this.slidesWithSameTitle.push(slide);
}
/**
* Whether or not this node's last child has the given title.
* @param {String} title the slide's title
*/
lastChildHasTitle(title) {
let len = this.children.length;
return len > 0 && this.children[len - 1].title == title;
}
/**
* Generate the HTML anchor content for this node (without any children).
*/
getLinkHTML() {
return `${this.title}`
}
/**
* Generate a list item for the topic list with a sublist of all child nodes.
*/
getHTML() {
let resultHTML = `
${this.getLinkHTML()}`;
if (this.children.length > 0) {
resultHTML +=
`