Add functionality to hide consecutive headings with the same title.

This commit is contained in:
Knoch 2020-03-26 13:46:16 +01:00
parent 5184c79147
commit a355060d91

View File

@ -35,12 +35,15 @@ function goToSlide(index) {
currentSlide = index;
window.location.href = window.location.href.split('#')[0] + `#slide${index}`;
let oldActiveTopicLink = topicList.querySelector(`.${activeTopicLinkClass}`);
if (oldActiveTopicLink != null) {
oldActiveTopicLink.classList.remove(activeTopicLinkClass);
}
let newActiveTopicLink = topicList.querySelector(`[data-slide="slide${index}"]`);
newActiveTopicLink.classList.add(activeTopicLinkClass);
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);
})
}
}
@ -114,36 +117,184 @@ function showPreviousFragment() {
}
}
/**
* 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 = '<ul>'
resultHTML += this.topLevelChildren
.map(root => root.getHTML())
.join('');
return resultHTML + '</ul>'
}
}
/**
* 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 `<a data-slide=";slide${this.slide};${
// Slide IDs have to be surrounded by »;«, otherwise slide10 and slide100
// would both be highlighted when slide10 is active.
this.slidesWithSameTitle.map(slide => `slide${slide}`).join(';')
};" href="javascript:goToSlide(${this.slide})" class="topic-link">${this.title}</a>`
}
/**
* Generate a list item for the topic list with a sublist of all child nodes.
*/
getHTML() {
let resultHTML = `<li>${this.getLinkHTML()}`;
if (this.children.length > 0) {
resultHTML +=
`<ul>${this.children.map(child => child.getHTML()).join('')}</ul>`
}
return resultHTML + '</li>';
}
}
function getTopicListContent() {
let results = [...document.querySelectorAll('h1, h2, h3, h4, h5, h6')];
let resultHtml = `<input type="text" class="${topicListSearchClass}"/><ul>`;
let resultHTML = `<input type="text" class="${topicListSearchClass}"/>`;
let currentLevel = 1;
headlineStore = new HeadlineStore();
currentNode = null;
results.forEach(element => {
let node = element.nodeName;
let level = parseInt(node[node.length - 1]);
let title = element.textContent;
let parentElement = element.parentElement
while (parentElement.nodeName.toLowerCase() != 'article') {
parentElement = parentElement.parentElement;
}
let parentSlide = parentElement.id;
let parentSlideID = parseInt(parentSlide.replace('slide', ''));
let linkHtml = `<a data-slide="${parentSlide}" href="javascript:goToSlide(${parentSlideID})" class="topic-link">${element.textContent}</a>`
let slide = parseInt(parentElement.id.replace('slide', ''));
if (level > currentLevel) {
resultHtml += `<ul><li>${linkHtml}</li>`;
currentLevel = level;
if (level == 1) {
// first chapter
let rootNode = new Node(title, level, null, slide);
headlineStore.add(rootNode)
currentNode = rootNode;
} else if (level > currentLevel) {
// one or more levels down
let childNode = new Node(title, level, currentNode, slide);
if (currentNode.addChildNode(childNode)) {
currentNode = childNode;
} else {
currentNode = currentNode.getLastChild();
currentNode.addSlideWithSameTitle(slide)
}
} else if (level == currentLevel) {
// same level
let parentNode = currentNode.father;
let siblingNode = new Node(title, level, parentNode, slide);
if (parentNode.addChildNode(siblingNode)) {
currentNode = siblingNode;
} else {
currentNode = parentNode.getLastChild();
currentNode.addSlideWithSameTitle(slide)
}
} else if (level < currentLevel) {
resultHtml += `</ul></li><li>${linkHtml}</li></li>`;
currentLevel = level;
} else {
resultHtml += `<li>${linkHtml}</a>`;
// one or more levels up
let parentNode = currentNode
while (parentNode.level >= level) {
parentNode = parentNode.father;
}
let siblingNode = new Node(title, level, parentNode, slide);
if (parentNode.addChildNode(siblingNode)) {
currentNode = siblingNode;
} else {
currentNode = parentNode.getLastChild();
}
}
})
return resultHtml + '</ul>';
currentLevel = level;
});
return resultHTML + headlineStore.getHTML();
}
function createTopicList() {