Jump to content

       

Go to Vaniquotes | Go to Vanipedia | Go to Vanimedia

Vanisource - the complete essence of Vedic knowledge

MediaWiki:VerseDrawer.js

From Vanisource

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* File 02: Vanisource verse drawer — BG 2.13 dev pilot */
(function () {
    'use strict';

    if (location.hostname !== 'dev.vanisource.org') return;
    if (mw.config.get('wgPageName') !== 'BG_2.13') return;
    if (mw.config.get('wgAction') !== 'view') return;

    mw.loader.using([
        'mediawiki.api',
        'mediawiki.util'
    ]).then(function () {
        mw.hook('wikipage.content').add(function ($content) {
            var verse = $content[0].querySelector('.verse');

            if (!verse || document.getElementById('vsd-open')) return;

            var api = new mw.Api();
            var loaded = false;
            var busy = false;
            var continuation = null;
            var scanned = 0;
            var matches = new Map();
            var requestId = 0;

            var style = document.createElement('style');
            style.textContent = `
                #vsd-open,
                .vsd button {
                    font: inherit;
                    cursor: pointer;
                    border: 1px solid #9d7860;
                    border-radius: 6px;
                    padding: 8px 12px;
                    background: white;
                    color: #51331f;
                }

                #vsd-open {
                    margin: 10px 0 18px;
                }

                .vsd[hidden] {
                    display: none !important;
                }

                .vsd {
                    position: fixed;
                    inset: 0 0 0 auto;
                    width: min(520px, 95vw);
                    background: #fffdf8;
                    color: #29231d;
                    box-shadow: -5px 0 24px #0003;
                    z-index: 10001;
                    display: flex;
                    flex-direction: column;
                    font: 16px/1.6 sans-serif;
                }

                .vsd header {
                    display: flex;
                    gap: 14px;
                    align-items: center;
                    padding: 18px 20px;
                    border-bottom: 1px solid #ddcdbb;
                    background: #f5ebdf;
                }

                .vsd h2 {
                    font-size: 21px;
                    margin: 0;
                    flex: 1;
                    color: #51331f;
                }

                .vsd-main {
                    overflow: auto;
                    padding: 20px;
                    flex: 1;
                    min-height: 0;
                }

                .vsd-note {
                    font-size: 13px;
                    color: #655b50;
                }

                .vsd-card {
                    border-bottom: 1px solid #ddcdbb;
                    padding: 16px 0;
                }

                .vsd-card button {
                    text-align: left;
                    width: 100%;
                    margin-bottom: 8px;
                }

                .vsd a {
                    color: #754325;
                }

                .vsd-text {
                    white-space: pre-wrap;
                    font-family: Georgia, serif;
                    line-height: 1.8;
                }

                .vsd-tools {
                    display: flex;
                    flex-wrap: wrap;
                    gap: 12px;
                    margin-bottom: 18px;
                }

                .vsd-selected {
                    outline: 2px solid #9d7860;
                    outline-offset: 7px;
                    cursor: pointer;
                }

                @media (min-width: 1200px) {
                    body.vsd-is-open {
                        padding-right: 520px;
                        box-sizing: border-box;
                    }
                }
            `;
            document.head.appendChild(style);

            function element(tag, text, className) {
                var node = document.createElement(tag);

                if (text !== undefined) node.textContent = text;
                if (className) node.className = className;

                return node;
            }

            var openButton = element(
                'button',
                'Lectures citing BG 2.13'
            );
            openButton.id = 'vsd-open';
            openButton.type = 'button';
            openButton.setAttribute('aria-expanded', 'false');
            openButton.setAttribute('aria-controls', 'vsd-panel');
            verse.after(openButton);

            verse.title = 'Explore lectures citing BG 2.13';

            var panel = element('aside', undefined, 'vsd');
            panel.id = 'vsd-panel';
            panel.hidden = true;
            panel.setAttribute('aria-labelledby', 'vsd-title');

            var header = element('header');
            var heading = element('h2', 'BG 2.13 · Lectures');
            heading.id = 'vsd-title';

            var closeButton = element('button', 'Close');
            closeButton.type = 'button';

            var main = element('div', undefined, 'vsd-main');

            header.append(heading, closeButton);
            panel.append(header, main);
            document.body.append(panel);

            function hide() {
                panel.hidden = true;
                document.body.classList.remove('vsd-is-open');
                verse.classList.remove('vsd-selected');
                openButton.setAttribute('aria-expanded', 'false');
                openButton.focus();
            }

            closeButton.onclick = hide;

            document.addEventListener('keydown', function (event) {
                if (event.key === 'Escape' && !panel.hidden) hide();
            });

            function show() {
                panel.hidden = false;
                document.body.classList.add('vsd-is-open');
                verse.classList.add('vsd-selected');
                openButton.setAttribute('aria-expanded', 'true');
                closeButton.focus();

                if (!loaded && !busy) load();
            }

            openButton.onclick = show;

            verse.addEventListener('click', function (event) {
                if (
                    !event.target.closest('a') &&
                    !window.getSelection().toString()
                ) {
                    show();
                }
            });

            function sourceLink(title) {
                var link = element('a', 'Open full page');
                link.href = mw.util.getUrl(title);
                link.target = '_blank';
                link.rel = 'noopener';

                return link;
            }

            function render(message) {
                requestId++;
                heading.textContent = 'BG 2.13 · Lectures';
                main.replaceChildren();

                main.append(element(
                    'p',
                    'Pilot: existing links filtered by lecture titles ' +
                    'and categories. Unlinked quotations are not included.',
                    'vsd-note'
                ));

                main.append(element(
                    'p',
                    matches.size + ' lecture pages found · ' +
                    scanned + ' linked pages checked.',
                    'vsd-note'
                ));

                if (message) main.append(element('p', message));

                Array.from(matches.values())
                    .sort(function (a, b) {
                        return a.title.localeCompare(b.title);
                    })
                    .forEach(function (page) {
                        var card = element(
                            'div',
                            undefined,
                            'vsd-card'
                        );

                        var readButton = element('button', page.title);
                        readButton.type = 'button';
                        readButton.onclick = function () {
                            transcript(page.title);
                        };

                        card.append(readButton, sourceLink(page.title));
                        main.append(card);
                    });

                if (!busy && (!loaded || continuation)) {
                    var moreButton = element(
                        'button',
                        loaded ? 'Check more linked pages' : 'Retry'
                    );
                    moreButton.type = 'button';
                    moreButton.onclick = load;
                    main.append(moreButton);
                }

                if (loaded && !continuation && !matches.size) {
                    main.append(element(
                        'p',
                        'No lecture pages were identified through these ' +
                        'links. This does not mean the verse was never cited.'
                    ));
                }
            }

            async function load() {
                if (busy) return;

                busy = true;
                render('Checking linked pages…');

                var parameters = {
                    action: 'query',
                    formatversion: 2,
                    generator: 'backlinks',
                    gbltitle: 'BG 2.13',
                    gblnamespace: 0,
                    gbllimit: 50,
                    prop: 'categories',
                    cllimit: 'max'
                };

                var next = continuation;
                var pending = new Map();

                try {
                    do {
                        var data = await api.get(
                            Object.assign({}, parameters, next || {})
                        );

                        var pages = (
                            data.query && data.query.pages
                        ) || [];

                        pages.forEach(function (page) {
                            var previous = pending.get(page.pageid);

                            if (previous) {
                                page.categories = (
                                    previous.categories || []
                                ).concat(page.categories || []);
                            }

                            pending.set(page.pageid, page);
                        });

                        next = data.continue || null;
                    } while (next && next.clcontinue);

                    pending.forEach(function (page) {
                        var isLecture = /\blecture\b/i.test(page.title);

                        if (!isLecture) {
                            isLecture = (page.categories || []).some(
                                function (category) {
                                    return /\blectures?\b/i.test(
                                        category.title
                                    );
                                }
                            );
                        }

                        if (isLecture && !page.redirect) {
                            matches.set(page.pageid, page);
                        }
                    });

                    scanned += pending.size;
                    continuation = next;
                    loaded = true;
                    busy = false;
                    render();
                } catch (error) {
                    busy = false;
                    render('The API request failed. Please retry.');
                }
            }

            async function transcript(title) {
                var ownRequest = ++requestId;

                heading.textContent = 'Lecture';
                main.replaceChildren();
                main.scrollTop = 0;

                var tools = element('div', undefined, 'vsd-tools');
                var backButton = element(
                    'button',
                    'Back to lecture list'
                );
                backButton.type = 'button';
                backButton.onclick = function () {
                    render();
                };

                tools.append(backButton, sourceLink(title));
                main.append(tools, element('h3', title));

                var status = element('p', 'Loading transcript…');
                main.append(status);

                try {
                    var data = await api.get({
                        action: 'parse',
                        page: title,
                        prop: 'text',
                        formatversion: 2,
                        disableeditsection: 1
                    });

                    if (ownRequest !== requestId) return;

                    var doc = new DOMParser().parseFromString(
                        data.parse.text,
                        'text/html'
                    );

                    doc.querySelectorAll(
                        'script,style,iframe,nav,' +
                        '.mw-editsection,.printfooter'
                    ).forEach(function (node) {
                        node.remove();
                    });

                    var root = (
                        doc.querySelector('.mw-parser-output') ||
                        doc.body
                    );

                    root.querySelectorAll(
                        'p,div,h2,h3,h4,li,br'
                    ).forEach(function (node) {
                        node.appendChild(doc.createTextNode('\n'));
                    });

                    var text = root.textContent
                        .replace(/\n[ \t]+/g, '\n')
                        .replace(/\n{3,}/g, '\n\n')
                        .trim();

                    status.remove();

                    var box = element(
                        'div',
                        undefined,
                        'vsd-text'
                    );

                    var index = text.toLowerCase().search(
                        /dehino|bg\s*2\.13/
                    );

                    if (index >= 0) {
                        var start = Math.max(0, index - 180);
                        var end = Math.min(text.length, index + 600);

                        box.append(document.createTextNode(
                            text.slice(0, start)
                        ));

                        var mark = element(
                            'mark',
                            text.slice(start, end)
                        );

                        box.append(
                            mark,
                            document.createTextNode(text.slice(end))
                        );
                        main.append(box);

                        requestAnimationFrame(function () {
                            if (ownRequest !== requestId) return;

                            var offset = (
                                mark.getBoundingClientRect().top -
                                main.getBoundingClientRect().top
                            );

                            main.scrollTop += offset - 100;
                        });
                    } else {
                        box.textContent = text;
                        main.append(box);
                    }
                } catch (error) {
                    if (ownRequest === requestId) {
                        status.textContent =
                            'Could not load the transcript. ' +
                            'Use Open full page.';
                    }
                }
            }
        });
    });
}());