Jump to content

User:Ladsgroup/dashboard.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// License: GPL-v3 or later
// Keep in sync with https://gitlab.wikimedia.org/ladsgroup/dashboard-gadget/-/blob/main/main.js

function dashboardGadget() {
    function msg( key ){
        const i18n = {
            'en': {
                'dashboard': 'Dashboard',
                'dashboard-message': 'Go to the dashboard',
                'loading': 'Loading...',
                'diff': 'Diff',
                'refresh': 'Refresh',
                'configuration': 'Configuration',
                'pending-since': 'Pending since',
                'default-config': 'User:Ladsgroup/defaultDashboardConfig.json'
            },
            'fa': {
                'dashboard': 'پیشخوان',
                'dashboard-message': 'رفتن به صفحهٔ پیشخوان عمومی',
                'loading': 'در حال بارگذاری...',
                'diff': 'تفاوت',
                'refresh': 'بروزرسانی',
                'configuration': 'تنظیمات',
                'pending-since': 'در حال انتظار از',
                'default-config': 'مدیاویکی:Gadget-dashboard/defaultConfig.json'
            },
            'de': {
                'dashboard': 'Dashboard',
                'dashboard-message': 'Go to the dashboard',
                'loading': 'Laden...',
                'diff': 'Unterschied',
                'refresh': 'Neuladen',
                'configuration': 'Einstellungen',
                'pending-since': 'Ungesichtet seit',
                'default-config': 'User:Ladsgroup/defaultConfig.json'
            }
        };
        return i18n[ mw.config.get( 'wgUserLanguage' ).split('-')[0] ][ key ] || i18n.en[ key ];
    }

    // inject the link
    if ($('.vector-user-links').length) {
    	// new vector
        let dashboardLinkDiv = $('<div></div>');
        dashboardLinkDiv.css('margin', '5px');
        let link = $('<a></a>');
        link.attr('href', '/wiki/Special:BlankPage/Dashboard');
        link.text(msg('dashboard'))
        dashboardLinkDiv.append(link);
        dashboardLinkDiv.append(' - ');
        $('.vector-user-links').prepend(dashboardLinkDiv);
    } else {
    	// old vector
        mw.util.addPortletLink('p-personal', '/wiki/Special:BlankPage/Dashboard', msg('dashboard'), 'p-dashboard', msg('dashboard-message'), null, '#pt-userpage');
    }

    if (
        mw.config.get( 'wgCanonicalNamespace' ) != 'Special' ||
        mw.config.get( 'wgCanonicalSpecialPageName' ) != 'Blankpage' ||
        mw.config.get( 'wgPageName' ).search( '/Dashboard' ) == -1
    ) {
        return;
    }
    document.documentElement.classList.remove('vector-feature-limited-width-clientpref-1');
    document.documentElement.classList.add('vector-feature-limited-width-clientpref-0');
    document.documentElement.classList.remove('vector-feature-page-tools-pinned-enabled');
    document.documentElement.classList.add('vector-feature-page-tools-pinned-disabled');
    $('.vector-page-toolbar').remove();
    window.dispatchEvent( new Event( 'resize' ) );
    $('.mw-first-heading').text(msg('dashboard'));
    document.title = msg('dashboard');
    $('.mw-body-content').text(msg('loading'));

    async function loadConfig( title ) {
        let response = await fetch("/wiki/" + encodeURIComponent(title) + '?action=raw');
        return response.text();
    }

    async function contentForPageType( box ) {
        let response = await fetch("/api/rest_v1/page/html/" + encodeURIComponent(box.pageTitle));
        return response.text();
    }

    async function contentForRC( box ) {
        let response = await fetch(box.url);
        let data = await response.json();
        data = data.query.recentchanges;
        let content = $('<ul></ul>');
        for ( i in data ) {
            let diff = $('<a></a>');
            diff.attr('href', '/wiki/Special:Diff/' + Number(data[i].revid));
            diff.text(msg('diff'));

            let liObj = $('<li></li>');
            liObj.append('(');
            liObj.append(diff);
            liObj.append(') - ');
            liObj.append(getPageLink(data[i].title));
            liObj.append(' ');
            liObj.append(getUser(data[i].user));
            liObj.append( ' (' + data[i].parsedcomment + ')' );

            content.append(liObj);
        }
        return content;
    }

    async function contentForWatchlist( box ) {
        let response = await fetch(box.url);
        let data = await response.json();
        data = data.query.watchlist;
        let content = $('<ul></ul>');
        for ( i in data ) {
            let diff = $('<a></a>');
            diff.attr('href', '/wiki/Special:Diff/' + Number(data[i].revid));
            diff.text(msg('diff'));

            let liObj = $('<li></li>');
            liObj.append('(');
            liObj.append(diff);
            liObj.append(') - ');
            liObj.append(getPageLink(data[i].title));
            liObj.append(' ');
            liObj.append(getUser(data[i].user));
            liObj.append( ' (' + data[i].parsedcomment + ')' );

            content.append(liObj);
        }

        return content;
    }

    async function contentForPending( box ) {
        let response = await fetch(box.url);
        let data = await response.json();
        data = data.query.oldreviewedpages;
        if ( !data ) {
            return 'Nothing to review!';
        }
        let content = $('<ul></ul>');
        for ( i in data ) {
            let diff = $('<a></a>');
            diff.attr('href', '/wiki/Special:Diff/' + Number(data[i].revid));
            diff.text(msg('diff'));

            let liObj = $('<li></li>');
            liObj.append('(');
            liObj.append(diff);
            liObj.append(') - ');
            liObj.append(getPageLink(data[i].title));
            liObj.append( ' (' + msg('pending-since') + ': ' + data[i].pending_since + ')' );

            content.append(liObj);
        }

        return content;
    }

    async function contentForCategory( box ) {
        let api = new mw.Api();
        if (box.norandom) {
            limit = 10;
        } else {
            limit = 100;
        }
        let data = await api.get( {
            action: 'query',
            list: 'categorymembers',
            cmprop: 'title',
            cmlimit: limit,
            cmtitle: box.categoryTitle
        } );
        data = getMultipleRandom(data.query.categorymembers, 10);
        let content = $('<ul></ul>');
        for ( i in data ) {
            let liObj = $('<li></li>');
            liObj.append(getPageLink(data[i].title));
            content.append(liObj);
        }
        return content;
    }

    async function contentForLinks( box ) {
        let content = $('<ul></ul>');
        for ( i in box.links ) {
            let link = box.links[i];
            if ( link.page ) {
                let liObj = $('<li></li>');
                liObj.append(getPageLink(link.page));
                content.append(liObj);
            } else if ( link.url ) {
                let link = $('<a>');
                link.attr('href', encodeURIComponent(link.url));
                link.text(link.text);
                let liObj = $('<li></li>');
                liObj.append(link);
                content.append(liObj);
            }
        }

        return content;
    }

    async function contnetForStats() {
        let api = new mw.Api();
        let data = await api.get( {
            action: 'query',
            meta: 'siteinfo',
            siprop: 'statistics',
        } );
        let content = '<ul>';
        content += '<li>تعداد مقالات: ' + mw.language.convertNumber(data.query.statistics.articles) + '</li>';
        content += '<li>تعداد صفحات: ' + mw.language.convertNumber(data.query.statistics.pages) + '</li>';
        content += '<li>تعداد ویرایش‌ها: ' + mw.language.convertNumber(data.query.statistics.edits) + '</li>';
        content += '<li>تعداد تصاویر: ' + mw.language.convertNumber(data.query.statistics.images) + '</li>';
        content += '<li>تعداد کاربران فعال: ' + mw.language.convertNumber(data.query.statistics.activeusers) + '</li>';
        return content + '</ul>';
    }

    async function contnetForCx() {
        let api = new mw.Api();
        let data = await api.get( {
            action: 'query',
            assert: 'user',
            list: 'contenttranslationsuggestions',
            from: 'en',
            to: 'fa',
            limit: 10,
            seed: Math.floor(Math.random() * 100)
        } );
        data = data.query.contenttranslationsuggestions.lists;
        let content = $('<ul></ul>');
        for ( i in data ) {
            for (j in data[i].suggestions) {
                let link = $('<a>');
                link.attr('href', 'https://en.wikipedia.org/wiki/' + encodeURIComponent(data[i].suggestions[j].title));
                link.text(data[i].suggestions[j].title);

                let liObj = $('<li></li>');
                liObj.append(link);
                content.append(liObj);
            }
        }
        let divObj = $('<div direction="ltr"></div>');
        divObj.append(content);
        return divObj;
    }

    const handlers = {
        'page': contentForPageType,
        'rc': contentForRC,
        'watchlist': contentForWatchlist,
        'category': contentForCategory,
        'pending': contentForPending,
        'links': contentForLinks,
        'stats': contnetForStats,
        'cx': contnetForCx,
    };

    function getUser(username) {
        let link = $('<a>');
        link.attr('href', '/wiki/User:' + encodeURIComponent(username));
        link.text(username);
        return link;
    }

    function getPageLink( title ) {
        let link = $('<a>');
        link.attr('href', '/wiki/' + encodeURIComponent(title));
        link.text(title);
        return link;
    }

    function getMultipleRandom(arr, num) {
        const shuffled = [...arr].sort(() => 0.5 - Math.random());
      
        return shuffled.slice(0, num);
    }

    function loadBoxes() {
        $('.mw-body-content').text(msg('loading'));
        let tabs = $('<div class="cdx-tabs"></div>');
        let tabsHeader = $('<div class="cdx-tabs__header"><div class="cdx-tabs__list"></div></div>');
        let tabsContent = $( '<div class="cdx-tabs__content"></div>');
        for ( let tabIndex in window.dashboardConfig.tabs ) {
            let tabConfig = window.dashboardConfig.tabs[tabIndex]; 
            let headerElement = $('<span class="cdx-tabs__list__item"></span>');
            headerElement.text( tabConfig.title );
            headerElement.on( 'click', () => { 
                window.dashboardActiveTab = tabIndex;
                loadBoxes();
            });
            if ( tabIndex == ( window.dashboardActiveTab || 0 ) ) {
                headerElement.attr('aria-selected', true);
            }
            tabsHeader.append(headerElement);
            
            let tabContent = $( '<div class="cdx-tab"></div>');
            tabContent.attr( 'id', 'tab-content-' + tabIndex );
            tabContent.css('display', 'flex');
            tabContent.css('flex-wrap', 'wrap');
            tabContent.css('align-items', 'flex-start');
            for (let i in tabConfig.boxes) {
                i = Number(i);
                let box = tabConfig.boxes[i];
                let element = $('<span class="cdx-card__text"></span>');
                let titleElement = $('<span class="cdx-card__text__title"></span>');
                titleElement.text(box.title);
                element.append(titleElement);
                let contentElement = $('<span></span>');
                contentElement.attr('class', 'cdx-card__text__supporting-text');
                contentElement.attr('id', 'dashboard-box-' + i);
                contentElement.append('<span class="cdx-css-icon--clock"></span>');
                contentElement.append(msg('loading'));
                contentElement.css('width', box.width || '300px' );
                contentElement.css('height', box.height || '300px' );
                contentElement.css('overflow', 'scroll' );
                element.append(contentElement);
                let wrapper = $( '<span class="cdx-card"></span>' );
                wrapper.append(element);
                wrapper.css('padding', '15px' );
                wrapper.css('margin', '15px' );
                const activeTab = window.dashboardActiveTab || 0;
                if ( tabIndex == activeTab ) {
                    wrapper.css('display', 'inline-flex');
                    tabsContent.append(wrapper);
                    if ( handlers[box.type] ) {
                        handlerPromise = handlers[box.type](box);
                    } else {
                        handlerPromise = new Promise(() => { return 'No handler has been found' } );
                    }
                    handlerPromise.then( ( content ) => { $( '#dashboard-box-' + i).html( content )});
                } else {
                    wrapper.css('display', 'none');
                }
            }
        }
        tabs.append(tabsHeader);
        let direction = 'left';
        if (document.dir == 'rtl') {
            direction = 'right';
        }

        let refreshButton = $('<button class="cdx-button cdx-button--action-progressive"></button>');
        refreshButton.append('<span class="cdx-button__icon cdx-css-icon--reload"></span>');
        refreshButton.text(msg('refresh'));
        refreshButton.css( 'align-self', 'flex-end' );
        refreshButton.css( 'margin-bottom', '5px' );
        refreshButton.css( 'margin-' + direction, '5px' );
        refreshButton.on('click', loadBoxes );
        
        let configButton = $('<button class="cdx-button cdx-button--action-progressive"></button>');
        configButton.append('<span class="cdx-button__icon cdx-css-icon--reload"></span>');
        configButton.text(msg('configuration'));

        let configLink = $('<a></a>');
        configLink.attr('href', '/wiki/ ' + encodeURIComponent( window.dashboardConfigLocation ));
        configLink.attr('target', '_blank');
        configLink.append(configButton);
        configLink.css( 'margin-bottom', '5px' );
        configLink.css( 'margin-' + direction, 'auto');   
        tabsHeader.append( configLink );
        tabsHeader.append( refreshButton );
        tabs.append(tabsContent);
        $('.mw-body-content').html(tabs);
    }
    let configPromise = null;
    if ( mw.util.getParamValue( 'dashboardConfig' ) ) {
        configPromise = loadConfig( mw.util.getParamValue( 'dashboardConfig' ) );
        window.dashboardConfigLocation = mw.util.getParamValue( 'dashboardConfig' );
    } else {
        configPromise = loadConfig('User:' + mw.config.get('wgUserName') + '/dashboardConfig.json' )
            .then( function (res) {
                try {
                    JSON.parse(res);
                    window.dashboardConfigLocation = 'User:' + mw.config.get('wgUserName') + '/dashboardConfig.json';
                    return res;
                } catch (erorr) {
                    window.dashboardConfigLocation = msg('default-config');
                    return loadConfig(msg('default-config'));
                }
            } );
    }
    configPromise.then( function (res) {
        let config = JSON.parse(res);
        window.dashboardConfig = config;
        mw.loader.using('codex-styles').then( loadBoxes() );
    });
};

(function ($, mw) {
    dashboardGadget();
}(jQuery, mediaWiki));