User:Bfdifan2006/protectionPadlocks.js

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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * This script is used to add padlocks to indicate the type, level, and expiry of page protection. Intended use is for MediaWiki.
 * 
 * To use this script, copy-paste mw.loader.load('https://bulbapedia.bulbagarden.net/w/index.php?title=User:Bfdifan2006/protectionPadlocks.js&action=raw&ctype=text/javascript'); into your custom JS!
 * 
 * Showcase: https://i.imgur.com/70X4nuT.png
 * 
 * @name protectionPadlocks
 * @summary Adds protection padlocks to pages.
 * @author Keyacom
 */

$(async () => {
  "use strict";
  const convertDateFormat = (isoDate) => {
    const dtc = new Date(isoDate);
    const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    const dotw = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    return `${dotw[dtc.getDay()]}, ${dtc.getDate()} ${months[dtc.getMonth()]}, ${dtc.getFullYear()}, ${dtc.getHours().toString().padStart(2,'0')}:${dtc.getMinutes().toString().padStart(2,'0')}`;
  }

  const getConf = mw.config.get.bind(mw.config);

  const addCSSLink = (href) => $('head').append(`<link rel="stylesheet" href="${href}">`);

  if (getConf('wgNamespaceNumber') != -1 && getConf('wgRestrictionEdit', getConf('wgRestrictionMove', getConf('wgRestrictionCreate'))).length !== 0) { // Exclude special pages, must be protected
    addCSSLink("https://bulbapedia.bulbagarden.net/w/index.php?title=User:Bfdifan2006/protectionPadlocks.css&action=raw&ctype=text/css");
    let indicators = $('<div class="mw-indicators"></div>');

    if (getConf('skin') == "minerva") { // Minerva does not have this by default
      $("#section_0").after(indicators);
    } else {
      indicators = $("div.mw-indicators");
    }

    const AUTOCONFIRMED = "autoconfirmed";
    const SYSOP = "sysop";
    const TITLE = "title";

    const padlockAutoconfirmed = $('<div class="padlock-autoconfirmed" title="This page is partially protected from editing$1.">A</div>');
    const padlockSysop = $('<div class="padlock-sysop" title="This page is fully protected from editing$1.">S</div>');
    const padlockMove = $('<div class="padlock-move" title="This page is fully protected from moving$1.">→</div>');
    const padlockCreate = $('<div class="padlock-create" title="This page is $2 protected from creation$1.">+</div>');

    // mw.config can only access page's protection level and type, so expiry must be fetched through a GET request
    let pageQuery = await fetch(`${getConf('wgServer')}${getConf('wgScriptPath')}/api.php?action=query&format=json&prop=info&titles=${encodeURIComponent(getConf('wgPageName'))}&inprop=protection`)
      .then(response => response.json())  // convert to json
      .catch(err => console.log('Request Failed', err));
    let articleId = getConf('wgArticleId').toString();
    if (articleId === "0") articleId = "-1";
    let protectionData = pageQuery.query.pages[articleId].protection // the output is an ARRAY

    for(let i of protectionData) {
      const {type, level, expiry} = i,
      EDIT = "edit",
      CREATE = "create",
      MOVE = "move",
      $1 = "$1",
      INFINITY = "infinity";
      let curPadlock;

      // This uses the fact objects are mutable
      if (type == EDIT && level == AUTOCONFIRMED) {
        curPadlock = padlockAutoconfirmed;
      } else if (type == EDIT && level == SYSOP) {
        curPadlock = padlockSysop;
      } else if (type == MOVE && level == SYSOP) {
        curPadlock = padlockMove;
      } else if (type == CREATE) {
        curPadlock = padlockCreate;
      }

      if (expiry !== INFINITY) {
        curPadlock.prop(TITLE, curPadlock.prop(TITLE).replace($1, ` until ${convertDateFormat(expiry)}`))
      } else {
        curPadlock.prop(TITLE, curPadlock.prop(TITLE).replace($1, ", indefinitely"))
      }
    }

    const editRestr = getConf('wgRestrictionEdit')?.[0];
    if (editRestr !== undefined) {
      if (editRestr === AUTOCONFIRMED) {
        indicators.append(padlockAutoconfirmed);
      }
      if (editRestr === SYSOP) {
        indicators.append(padlockSysop);
      }
      if (getConf('wgRestrictionMove')[0] === SYSOP && editRestr !== SYSOP) {
        indicators.append(padlockMove);
      }
    }
    const createRestr = getConf('wgRestrictionCreate')?.[0];
    if (createRestr !== undefined) {
      const $2 = "$2"
      if (createRestr === SYSOP) {
        padlockCreate.prop(TITLE ,padlockCreate.prop(TITLE).replace($2, "fully"));
      } else if (createRestr === AUTOCONFIRMED) {
        padlockCreate.prop(TITLE ,padlockCreate.prop(TITLE).replace($2, "partially"));
      }
      if (getConf('wgRestrictionCreate').length === 0) {
        indicators.append(padlockCreate);
      }
    }
  }
});