MediaWiki:VanguardTools.js

/** *  * @module                  VanguardTools * @description            Accelerates Vanguard migration workflow. * @author                 Speedit * @version                1.1.0 * @license                CC-BY-SA 3.0 * */ require(['wikia.window', 'jquery', 'mw', 'wikia.nirvana', 'ext.wikia.design-system.loading-spinner'], function (window, $, mw, nirvana, Spinner) {    'use strict';

// Script variables and double run protection if (window.vanguardToolsLoaded) { return; }   window.vanguardToolsLoaded = true; var VAN = {};

// MediaWiki variables VAN.mw = mw.config.get([       'wgCityId',        'wgCanonicalNamespace',        'wgCanonicalSpecialPageName',        'wgTitle',        'wgArticleId',        'wgUserGroups',        'skin'    ]);

// User parrot status VAN.parrot = VAN.mw.wgUserGroups.some(function(ug) {       return ['vanguard', 'staff'].indexOf(ug) > -1;    });

// Script configuration VAN.config = window.vanguardToolsConfig || { state: true, modules: [] };   if (VAN.config.modules && !$.isArray(VAN.config.modules)) { delete VAN.config.modules; }

// Design system integration VAN.wds = { // Library handler handler: function(wds) { // Cache our design system utils $.extend(VAN.wds, wds); // Asset loading done VAN.wds.$loaded.resolve; },       // Asset loading event $loaded: $.Deferred };

// I18n implementation VAN.i18n = { // Message & utility loader handler: function(i18no) { i18no.loadMessages('VanguardTools').done(VAN.i18n.store); },       // Message data handler store: function(i18n) { // Cache our msg data, utils $.extend(VAN.i18n, i18n); // Message loading done VAN.i18n.$loaded.resolve; },       // I18n event $loaded: $.Deferred };

// Main script VAN.init = function { if (!VAN.parrot || !VAN.config.state || VAN.mw.skin !== 'oasis') { return; } // Dispatch dependency handlers mw.hook('dev.i18n').add(VAN.i18n.handler); mw.hook('dev.wds').add(VAN.wds.handler); // Module activator $.when(VAN.i18n.$loaded, VAN.wds.$loaded).then(function {           // Run all modules by default            VAN.config.modules = VAN.config.modules.length > 0 ?                VAN.config.modules :                VAN.modules;            // Validation for modules            $.each(VAN.config.modules, function(i, m) { if (VAN.modules.indexOf(m) === -1) { VAN.modules.splice(i, 1); }           });            // Initialise all configured modules            $.each(VAN.config.modules, function(i, m) { VAN[m].init; });           // Post-execution event            $.when.apply(null, VAN.config.modules.map(function(m) {                return VAN[m].$executed;            })).then(function { // Loaded class $(document.body).addClass('van-is-loaded'); // Fire hook mw.hook('dev.van').fire(VAN); });       });        // Import dependencies importArticle({ type: 'script', article: 'u:dev:I18n-js/code.js' }); importArticle({ type: 'script', article: 'u:dev:WDSIcons/code.js' }); };

// Redirect module for S:I/NPI VAN.redirect = { init: function { if (               VAN.mw.wgCanonicalSpecialPageName !== 'InfoboxBuilder'            ) { VAN.redirect.$executed.resolve; return; }           // Spinner code VAN.redirect.spinner = $(' ').css({                   background:                        $(document.body).css('background-color')                            .replace('rgb','rgba').replace(')', ', 0.5)'),                    position: 'fixed',                    height: '100%',                    width: '100%',                    left: '0',                    top: '0',                    'z-index': '1000000000'                }).html(function {                    return new Spinner(38, 2).html                        .replace('wds-block', 'wds-spinner__block')                        .replace('wds-path', 'wds-spinner__stroke');                }).appendTo(document.body); // Template existence check VAN.redirect.template = mw.util.wikiUrlencode(VAN.mw.wgTitle.match(/\/([\s\S]+)$/)[1]); nirvana.getJson('PortableInfoboxBuilderController', 'getTemplateExists', {               title: VAN.redirect.template            }, VAN.redirect.builder); },       // Infobox builder template creation builder: function(t) { if (t.exists) { VAN.redirect.execute; } else if (!VAN.redirect.hasOwnProperty('opt')) { VAN.redirect.opt = window.confirm(VAN.i18n.msg('sourceredirect').plain); VAN.redirect.builder(t); } else if (VAN.redirect.hasOwnProperty('opt') && !VAN.redirect.opt) { VAN.redirect.$executed.resolve; return; } else { nirvana.sendRequest({                   controller: 'PortableInfoboxBuilderController',                    method: 'publish',                    data: {                        data: JSON.stringify({ "data": [ {                                   "data": { "defaultValue": "" }, "source": "name", "type": "title" },                               {                                    "data": { "caption": { "source": "caption" }}, "source": "image", "type": "image" },                               {                                    "data": VAN.i18n.msg('piheader').plain, "collapsible": false, "type": "section-header" },                               {                                    "data": {"label": VAN.i18n.msg('pitype').plain }, "source": "type", "type": "row", "sourceFrozen": false }                           ]                        }),                        title: VAN.redirect.template,                        oldTitle: VAN.redirect.template,                        token: mw.user.tokens.get('editToken')                    },                    callback: VAN.redirect.callback                }); }       },        // Infobox builder redirection callback: function(d) { if (d.success) { VAN.redirect.execute; } else { VAN.redirect.init; }       },        execute: function { var uri = new mw.Uri(               mw.util.wikiGetlink('Template:' + VAN.redirect.template)            ); uri.extend({               action: 'edit',                useeditor: 'source'            }); window.location.href = uri.toString; VAN.redirect.$executed.resolve; },       $executed: $.Deferred };

// Global navigation links module VAN.nav = { init: function { var $l = $('').addClass('wds-list wds-is-linked'); $.each(VAN.nav.uri, function(link) {               $l.append( $('').append(                       $('', { 'href': '/wiki/' + VAN.nav.uri[link], text: VAN.i18n.msg(link).plain })                   )                );            });            // Icon and dropdown generation var icon = VAN.wds.icon('menu-control-tiny'); icon.classList.add('wds-dropdown-chevron'); var $c = VAN.nav.dropdown($l, icon); // Append to menu $('.wds-global-navigation__user-menu') // Prevent scrollable dropdown .children('.wds-dropdown__content') .addClass('wds-is-not-scrollable') // Insert dropdown .find('.wds-list > li:first-child').after($c); VAN.nav.$executed.resolve; },       // Create dropdown dropdown: function($l, icon) { return $('', {               'id': 'van-tools-dropdown',                'class': 'wds-dropdown-level-2'            }).append(                $('', { 'href': mw.util.wikiGetlink(VAN.nav.uri.insights), 'target': '_blank', 'rel': 'noopener noreferrer', 'class': [ 'wds-dropdown-level-2__toggle', 'van-tools-link' ].join(' ') }).append( $(' ', {                       text: VAN.i18n.msg('tools').plain                    }), icon ),               $(' ', {                    'class': [ 'wds-dropdown-level-2__content', 'wds-is-not-scrollable', 'van-tools-menu' ].join(' ') }).append($l)           ); },       // Global navigation links uri: { insights:      'Special:Insights/nonportableinfoboxes', infoboxes:     'Special:Templates?type=infobox', templates:     'Special:Templates', sitecss:       'Special:CSS', personalcss:   'Special:MyPage/common.css', themescss:     'MediaWiki:Themes.css?action=edit', admins:        'Special:ListUsers/sysop', wikifeatures:  'Special:WikiFeatures' },       $executed: $.Deferred };

// Template reclassification hotkey module VAN.template = { init: function { if (VAN.mw.wgCanonicalNamespace !== 'Template') { VAN.template.$executed.resolve; return; }           var $popout = $(' ', {                'class': 'van-popout'            }).append(                $('', { text: VAN.i18n.msg('templatetypeguide').plain }),               $('').append( $.map(VAN.template.types, function(o, k) {                       return $('').append( $(' ', {                               text: o.key                            }), $(' ', {                               text: VAN.template.labels[o.type]                            }) );                   })                )            );            $popout.appendTo(document.body); // Delegate keyboard handler $(document).keyup(VAN.template.shortcut); VAN.template.$executed.resolve; },       // Shortcut keys shortcut: function(e) { if (               !VAN.template.types.hasOwnProperty(e.which) ||                !e.altKey            ) { return; }           VAN.template.type = VAN.template.types[e.which].type, VAN.template.label = VAN.template.labels[VAN.template.type]; nirvana.postJson(               'TemplateClassificationApi',                'classifyTemplate',                {                    pageId: mw.config.get('wgArticleId'),                    type: VAN.template.type,                    editToken: mw.user.tokens.values.editToken                },                VAN.template.success            ); },       // Callback for TemplateClassification controller success: function { $('.template-classification-type-label').text(VAN.template.label); var message = VAN.i18n.msg('templatetypechange', VAN.template.label).plain; notification = new BannerNotification(message, 'confirm'); notification.show; },       // Template type map for keyboard shortkeys types: { 223: { key: '`', type: 'infobox' }, 49: { key: '1', type: 'quote' }, 50: { key: '2', type: 'navbox'}, 51: { key: '3', type: 'notice' }, 52: { key: '4', type: 'context-link' }, 53: { key: '5', type: 'infoicon' }, 54: { key: '6', type: 'scrollbox' }, 55: { key: '7', type: 'references' }, 56: { key: '8', type: 'media' }, 57: { key: '9', type: 'data' }, 48: { key: '0', type: 'design' }, 189: { key: '-', type: 'navigation' }, 187: { key: '=', type: 'nonarticle' } },       // Template labels for template types labels: { 'infobox':     'Infobox', 'quote':       'Quote', 'navbox':      'Navbox', 'notice':      'Notice', 'context-link': 'Context-link', 'infoicon':    'Infoicon', 'scrollbox':   'Scrollbox', 'references':  'References', 'media':       'Media', 'data':        'Data', 'design':      'Design', 'navigation':  'Navigation', 'nonarticle':  'Non-article' },       $executed: $.Deferred };

// S:I/NPI utility extension. VAN.insights = { // Module initialiser init: function { if (               !$('.insights-nav-item.insights-icon-nonportableinfoboxes.active')                    .find('.insights-red-dot').exists            ) { VAN.insights.$executed.resolve; return; }           // Data lists. VAN.insights.list = {}; var chunks = []; [].slice.call($('.insights-list-item-title')).map(function(e, i) {               // Template name.                var t = e.innerText;                $(e).closest('.insights-list-item')                    .attr('data-template', t);                // API data.                VAN.insights.list[t] = {};                ( chunks[Math.floor(i / 50)] = chunks[Math.floor(i / 50)] || []               )[i % 50] = t;            }); VAN.insights.api = new mw.Api; chunks.forEach(function(c) {               VAN.insights.api.get({ 'action': 'query', 'prop': 'info', 'inprop': 'protection', 'titles': c.join('|') }).done(VAN.insights.handler);           }); },       // Template list list: {}, // API handler handler: function(d) { $.each(d.query.pages, function(id, data) {               VAN.insights.list[data.title].protection =                    data.protection.filter(function(obj) { return (obj.type === 'edit'); }).length > 0;           }); if (Object.keys(VAN.insights.list).every(function(t) { return VAN.insights.list[t].hasOwnProperty('protection'); })) {               VAN.insights.ui; }       },        // Interface configuration map for actions map: { 'viewdraft': { icon: 'eye-small' },           'conversion': { icon: 'gear-small' },           'rawcode': { icon: 'article-small', query: { action: 'raw', ctype: 'text/css' // Firefox fix }           },            'protected': { icon: 'lock-small', query: { action: 'history' }           },            'unprotected': { icon: 'unlock-small', query: { action: 'history' }              }        },        // UI modification ui: function { // Add page header class (hack for button colors) $('.insights-content').addClass('page-header'); // Button map for WDS icon addition $('.insights-list-cell-altaction .wikia-button').each(function {               var $b = $(this),                    t = $(this).closest('.insights-list-item')                        .attr('data-template');                VAN.insights.list[t].$toolbar =                    $(' ', { 'class': [ 'wds-button-group', 'van-action-button-group' ].join(' ') }).insertBefore($b).append($b);               // Class addition                $b                    .removeAttr('class')                    .addClass([ 'wds-button', 'wds-is-squished' ].join(' '))                   .addClass(function(i, c) { if ($b.attr('href').indexOf('action=edit') > -1) { return [ 'van-action-button', 'van-is-conversion' ].join(' '); } else { return [ 'wds-is-secondary', 'van-action-button', 'van-is-viewdraft' ].join(' '); }                   }).html(function(i, html) { return ' ' + mw.html.escape(html) + ' '; });               // List buttons                VAN.insights.list[t].$buttons = [$b];                // Generate toolbar                VAN.insights.tbr(t);            }); },       // Template toolbar generator tbr: function(t) { var b = { 'unprotected': false, 'protected': true },               $t = VAN.insights.list[t].$toolbar; // Button creation $.each(VAN.insights.map, function(l, c) {               // Prevent duplication                if ( !['conversion', 'viewdraft'].some(function(s) {                       return l === s;                    }) && (                       Object.keys(b).indexOf(l) === -1 ||                        VAN.insights.list[t].protection === b[l]                    ) ) {                   // Button element                    var $b = $('', { 'href': (function {                               var uri = new mw.Uri(mw.util.wikiGetlink(t));                                // Add query                                if (c.query) {                                    uri.extend(c.query);                                }                                return uri.toString;                            }), html: $(' ', {                               text: VAN.i18n.msg(l).plain                            }) })                       .addClass([ 'wds-button', 'wds-is-squished', 'wds-is-secondary', 'van-tools-button', 'van-is-' + l                       ].join(' '))                        .appendTo(VAN.insights.list[t].$toolbar);                    // Cache button                    VAN.insights.list[t].$buttons.push($b);                }                // Icon addition                $t.children('.van-is-' + l)                    .prepend(VAN.wds.icon(c.icon));            }); VAN.insights.$executed.resolve; },       $executed: $.Deferred };

// S:PortabilityDashboard utility extension. VAN.pdash = { init: function { if (               VAN.mw.wgCityId !== '1230494' ||                VAN.mw.wgCanonicalSpecialPageName !== 'PortabilityDashboard'            ) { VAN.pdash.$executed.resolve; return; }           var $p = $('.portability-dashboard-table'); $(' ', {               'class': 'headerSort',                'title': $('th.headerSort').attr('title'),                append: [                    $(' ', { 'class': 'tooltip-icon-wrapper', text:   'Rank' }),                   $(' ', {                        html: ' '.repeat(2) })               ],                click: VAN.pdash.headerSort,            }).prependTo('.portability-dashboard-table thead tr'); $p.find('thead th') .click(VAN.pdash.headerSort); $p.find('tbody tr').each(function(i) {               $(' ', { text: (i + 1) }).prependTo(this);           }); VAN.pdash.$executed.resolve; },       headerSort: function { var f = !$(this).prev.length, l = !$(this).next.length, $f = f ? $(this) : $(this).siblings(':first'), $l = l ? $(this) : $(this).siblings(':last'); f ? $l.click : $f.attr('class', $l.attr('class')); },       $executed: $.Deferred };

// Module registry VAN.modules = Object.keys(VAN).filter(function(m) {       return ( typeof VAN[m] === 'object' && // class check VAN[m].init                  // initializer check );   });

// Import styling importArticle({       type: 'style',        article: 'u:dev:MediaWiki:VanguardTools.css'    });

// Script initializer mw.loader.using([       'mediawiki.util',        'mediawiki.api',        'ext.bannerNotifications'    ]).then(VAN.init);

}); /** **/