mdbook-presentation-preprocessor 0.1.0

A preprocessor for utilizing an MDBook as slides for a presentation.
let ___presentationModeHider;
window.addEventListener('DOMContentLoaded', () => {
    if (!___presentationModeHider) {
        ___presentationModeHider = new PresentationModeHider();
    }
});


enum PresentationMode {
    Slides = 0,
    Web = 1,
}

class PresentationModeHider {

    private mode: PresentationMode;
    private queryKey = 'presentation_mode=';

    constructor() {
        this.mode = this.getMode();
        this.setMode();
        this.updatePage();
        if (this.mode === PresentationMode.Slides && (
            location.pathname === ''
            || location.pathname === '/')
            ) {
            let chList = document.querySelector('.sidebar > .chapter') as HTMLUListElement;
            let firstLi = chList.firstChild as HTMLLIElement;
            let firstLink = firstLi.firstChild as HTMLAnchorElement;
            firstLink.click();
        }
        window.addEventListener('popstate', () => {
            this.mode = this.getMode();
            this.setMode();
            this.updatePage();
        });
        window.addEventListener('keyup', ev => {
            if (!ev.altKey) return;
            if (ev.key == 'p' || ev.key == 'P' || ev.code == 'KeyP') {
                this.toggle();
            }
        });
    }
    private getMode(): PresentationMode {
        // if there was no query, we are on the web
        if (location.search == '') {
            return PresentationMode.Web;
        }
        let modeIdx = location.search.indexOf(this.queryKey);
        // if the query doesn't have our key, we are on the web
        if (modeIdx < 0) {
            return PresentationMode.Web;
        }
        let modeValue = location.search.substr(modeIdx + this.queryKey.length, 1);
        try {
            let mode = parseInt(modeValue);
            // if the mode is invalid, fallback to the web
            if (mode > 1) {
                return PresentationMode.Web;
            }
            return mode;
        } catch (e) {
            // if the mode is invalid, fallback to the web
            console.error(e);
            return PresentationMode.Web;
        }
    }

    private setMode() {
        let newUrl = this.updateQuery(location.href);
        history.replaceState({}, '', newUrl);
    }

    private getQuery() {
        return this.queryKey + this.mode;
    }

    private updatePage() {
        let cls =  '.presentation-only';
        let elements = document.querySelectorAll(cls);
        this.updateElements(elements);
        this.updateElements(document.querySelectorAll('.article-content'))
        this.updateLinks();
    }
    
    private updateElements(elements) {
        for (var i = 0; i < elements.length; i++) {
            let el = elements[i];
            let elCls = el.getAttribute('class');
            if (this.mode === PresentationMode.Slides) {
                el.setAttribute('class', this.swapClass('presenting', 'not-presenting', elCls));
            } else {
                el.setAttribute('class', this.swapClass('not-presenting', 'presenting', elCls));
            }
        }
    }

    private swapClass(add: string, remove: string, old: string): string {
        let split = old.split(' ');
        let addIdx = null;
        split = split.filter((c, i) => {
            if (c == add) {
                addIdx = i;
            }
            return c != remove;
        });
        if (addIdx === null) {
            split.push(add)
        }
        return split.join(' ');
    }

    private addClass(add: string, old: string): string {
        let split = old.split(' ');
        if (split.indexOf(add) > -1) {
            return old;
        }
        split.push(add);
        return split.join(' ');
    }
    private removeClass(remove: string, old: string): string {
        let split = old.split(' ');
        let idx = split.indexOf(remove);
        if (idx < 0) {
            return old;
        }
        split.splice(idx, 1);
        return split.join(' ');
    }
    private updateLinks() {
        let links = document.getElementsByTagName('a');
        for (var i = 0; i < links.length; i++) {
            links[i].href = this.updateQuery(links[i].href)
        }
    }

    private updateQuery(q: string): string {
        let modeIdx = q.indexOf(this.queryKey);
        if (modeIdx > -1) {
            var currentQuery = q.substr(modeIdx, this.queryKey.length + 1);
            return q.replace(currentQuery, this.getQuery());
        } else {
            if (q.indexOf('?') < 0) {
                return q + '?' + this.getQuery();
            }
            return q + '&' + this.getQuery();
        }
    }

    private toggle() {
        switch (this.mode) {
            case PresentationMode.Slides:
                this.mode = PresentationMode.Web;
                break;
            case PresentationMode.Web:
                this.mode = PresentationMode.Slides;
                break;
        }
        this.setMode();
        this.updatePage();
    }
}