MediaWiki:Common.js: Difference between revisions

From OniGalore
(I broke the collapsible table code, probably by deleting a block of code that was labeled "experimental", but let's try the newest code from Wikipedia before restoring that)
(finished TODO item)
 
(2 intermediate revisions by the same user not shown)
Line 22: Line 22:
/** &withJS= URL parameter *******
/** &withJS= URL parameter *******
  * Allow to try custom scripts from MediaWiki space  
  * Allow to try custom scripts from MediaWiki space  
  * without editing [[Special:Mypage/monobook.js]]
  * without editing [[Special:Mypage/skinname.js]]
  */
  */
var extraJS = getURLParamValue("withJS");
var extraJS = getURLParamValue("withJS");
Line 30: Line 30:
}
}


// TODO: Get this working again
/* Modify the Search page to offer alternative search engines by loading [[MediaWiki:Common.js/search.js]] */
/* If we're on the Search page, load the script that augments that page */
mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript');
if (mw.config.get('wgPageName') === 'Special:Search')
{
    mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript');
}


/**
/**
Line 203: Line 199:
$(initHoverGIFs);
$(initHoverGIFs);


/***** autosort_sortable ********
/***** Auto-sorting tables ********
  * Auto-sorts sortable tables by a given column (why is this not built-in?!)
  * Auto-sorts sortable tables by a given column (why is this not built-in?!)
  * You must opt into this feature by placing "autosort" in the list of
  * You must opt into this feature by placing "autosort" in the list of
Line 278: Line 274:
}
}
$(sortTimer);
$(sortTimer);
/***** Auto-formatting dates ********
* Re-formats a date placed by [[Template:LocaleDate]] to match the
* month/day order of the user's locale.
*
*  Maintainers: [[User:Iritscen]]
*/
function getLang()
{
  if (navigator.languages !== undefined)
    return navigator.languages[0];
  return navigator.language;
}
/* Because CSS variables didn't seem to work for changing the .date-[day/month]::before rules,
  this function manually trawls the stylesheets until it finds those rules and changes them */
function changeDateSeparator(component, separator)
{
  const styleSheets = document.styleSheets;
  var ruleIndex;
 
  for (var i = 0; i < styleSheets.length; i++)
  {
    const rules = styleSheets[i].cssRules || styleSheets[i].rules;
    for (var j = 0; j < rules.length; j++)
    {
      if (rules[j].selectorText === '.mw-parser-output .date-'+component+'::before')
      {
        ruleIndex = j;
        break;
      }
    }
    if (ruleIndex !== undefined)
    {
      // Update the content of the existing rule
      styleSheets[i].deleteRule(ruleIndex);
      styleSheets[i].insertRule('.date-'+component+'::before{content:"'+separator+'";}', ruleIndex);
      break;
    }
  }
}
function localizeDates()
{
  const lang = getLang();
  const root = document.querySelector(':root');
  if (root == undefined || lang == undefined)
    return;
  if (lang != "en-US") // Template:LocaleDate prints dates in U.S.-style by default
  {
    root.style.setProperty('--month-order', '2');
    root.style.setProperty('--day-order', '1');
    changeDateSeparator('month', '/');
    changeDateSeparator('day', '');
  }
}
$(localizeDates);

Latest revision as of 03:43, 28 December 2025

/* JavaScript brought over from Wikipedia appears first. JS code written for our wiki comes second, under the heading CUSTOM ONI WIKI ADDITIONS. */

/* WIKIPEDIA CARRY-OVERS */
/* The original code on Wikipedia's Common.js page should be checked once in a while for updates to, or removal of, this code */

/** extract a URL parameter from the current URL **********
 * From [[en:User:Lupin/autoedit.js]]
 *
 * paramName  : the name of the parameter to extract
 */
function getURLParamValue(paramName, url) 
{
    if (typeof (url) == 'undefined' || url === null)
        url = document.location.href;
    var cmdRe = RegExp('[&?]' + paramName + '=([^&#]*)'); // Stop at hash
    var m = cmdRe.exec(url);
    if (m && m.length > 1)
        return decodeURIComponent(m[1]);
    return null;
}

/** &withJS= URL parameter *******
 * Allow to try custom scripts from MediaWiki space 
 * without editing [[Special:Mypage/skinname.js]]
 */
var extraJS = getURLParamValue("withJS");
if (extraJS && extraJS.match("^MediaWiki:[^&<>=%]*\.js$"))
{
    mw.loader.load('/w/index.php?title=' + extraJS + '&action=raw&ctype=text/javascript');
}

/* Modify the Search page to offer alternative search engines by loading [[MediaWiki:Common.js/search.js]] */
mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript');

/**
 * Collapsible tables; reimplemented with mw-collapsible
 * Styling is also in place to avoid FOUC
 *
 * Allows tables to be collapsed, showing only the header. See [[Help:Collapsing]].
 * @version 3.0.0 (2018-05-20)
 * @source https://www.mediawiki.org/wiki/MediaWiki:Gadget-collapsibleTables.js
 * @author [[User:R. Koot]]
 * @author [[User:Krinkle]]
 * @author [[User:TheDJ]]
 * @deprecated Since MediaWiki 1.20: Use class="mw-collapsible" instead which
 * is supported in MediaWiki core. Shimmable since MediaWiki 1.32
 *
 * @param {jQuery} $content
 */
function makeCollapsibleMwCollapsible( $content ) {
   var $tables = $content
      .find( 'table.collapsible:not(.mw-collapsible)' )
      .addClass( 'mw-collapsible' );

   $.each( $tables, function ( index, table ) {
      // mw.log.warn( 'This page is using the deprecated class collapsible. Please replace it with mw-collapsible.');
      if ( $( table ).hasClass( 'collapsed' ) ) {
         $( table ).addClass( 'mw-collapsed' );
         // mw.log.warn( 'This page is using the deprecated class collapsed. Please replace it with mw-collapsed.');
      }
   } );
   if ( $tables.length > 0 ) {
      mw.loader.using( 'jquery.makeCollapsible' ).then( function () {
         $tables.makeCollapsible();
      } );
   }
}
mw.hook( 'wikipage.content' ).add( makeCollapsibleMwCollapsible );

/**
 * Add support to mw-collapsible for autocollapse, innercollapse and outercollapse
 *
 * Maintainers: TheDJ
 */
function mwCollapsibleSetup( $collapsibleContent ) {
   var $element,
      $toggle,
      autoCollapseThreshold = 2;
   $.each( $collapsibleContent, function ( index, element ) {
      $element = $( element );
      if ( $element.hasClass( 'collapsible' ) ) {
         $element.find( 'tr:first > th:first' ).prepend( $element.find( 'tr:first > * > .mw-collapsible-toggle' ) );
      }
      if ( $collapsibleContent.length >= autoCollapseThreshold && $element.hasClass( 'autocollapse' ) ) {
         $element.data( 'mw-collapsible' ).collapse();
      } else if ( $element.hasClass( 'innercollapse' ) ) {
         if ( $element.parents( '.outercollapse' ).length > 0 ) {
            $element.data( 'mw-collapsible' ).collapse();
         }
      }
      // because of colored backgrounds, style the link in the text color
      // to ensure accessible contrast
      $toggle = $element.find( '.mw-collapsible-toggle' );
      if ( $toggle.length ) {
         // Make the toggle inherit text color (Updated for T333357 2023-04-29)
         if ( $toggle.parent()[ 0 ].style.color ) {
            $toggle.css( 'color', 'inherit' );
            $toggle.find( '.mw-collapsible-text' ).css( 'color', 'inherit' );
         }
      }
   } );
}

mw.hook( 'wikipage.collapsibleContent' ).add( mwCollapsibleSetup );

/* CUSTOM ONI WIKI ADDITIONS */

/* Focus us on the Search box if we're on the Main Page, so we can immediately start typing */
if (mw.config.get('wgIsMainPage'))
{
    $(function()
    {
        $('#searchInput').focus();
    });
}

/** Hover tables *********************************************************
 *
 *  Description: Allows tables to use mouse-over to provide explanation of
 *               the pointed-to content.
 *  Maintainers: [[User:Iritscen]]
 */
function showDescrip(typeID, show_or_not)
{
    var DescripPanel = document.getElementsByClassName("hovertable_descrip")[0]; /* only get 1st one on page to keep things simple */
    var Descrips = DescripPanel.getElementsByTagName("span");

    if (!DescripPanel || !Descrips)
        return false;

    for (var i = 0; i < Descrips.length; i++)
    {
        if (Descrips[i].id == typeID)
        {
            if (show_or_not)
                Descrips[i].style.display = "block";
            else
                Descrips[i].style.display = "none";
        }
    }
}
function initHoverTables()
{
    var Tables = document.getElementsByClassName("hovertable");
    if (!Tables)
        return false;

    for (var i = 0; i < Tables.length; i++)
    {
        var Cells = Tables[i].getElementsByTagName("td");
        if (!Cells) continue;

        for (var j = 0; j < Cells.length; j++)
        {
            if ($(Cells[j]).hasClass("hovercell"))
            {
                $(Cells[j]).mouseover(new Function("evt", "showDescrip(this.id, true);"));
                $(Cells[j]).mouseout(new Function("evt", "showDescrip(this.id, false);"));
            }
        }
    }
}
$(initHoverTables);

/** Hover GIFs *********************************************************
 *
 *  Description: Allows GIFs to only animate upon mouse-over.
 *  
 *  Maintainers: [[User:Iritscen]]
 */
function swapImage(gifID, show_gif)
{
    var gif_span = document.getElementById(gifID);
    var gif_image = gif_span.getElementsByClassName("image")[0];
    var gif_img = gif_image.getElementsByTagName("img")[0];
    var gif_src = gif_img.src;

    if (!gif_img)
        return false;

    if (show_gif)
        gif_img.src = gif_src.replace('.jpg', '.gif');
    else
        gif_img.src = gif_src.replace('.gif', '.jpg');
}
function initHoverGIFs()
{
    var gifs = document.getElementsByClassName("hovergif");

    if (!gifs)
        return false;

    for (var i = 0; i < gifs.length; i++)
    {
        $(gifs[i]).mouseover(swapImage(this.id, true));
        $(gifs[i]).mouseout(swapImage(this.id, false));
    }
}
$(initHoverGIFs);

/***** Auto-sorting tables ********
 * Auto-sorts sortable tables by a given column (why is this not built-in?!)
 * You must opt into this feature by placing "autosort" in the list of
 * the table's classes along with "sortable"
 *
 *  Maintainers: [[User:Iritscen]]
 */
// For some reason, the arrow buttons and headerSort classes are not attached to sortable tables immediately upon the "load" event, and we need those, so we wait a bit before trying to sort
function sortTimer()
{
    setTimeout(function()
    {
        sortSortableTables();
    }, (1 * 1000));
}
function sortSortableTables()
{
    console.log("Sorting the autosort tables.");
    // Iterate over all <div> elements
    var divs = document.getElementsByTagName("div");
    if (!divs) return;
    for (var i = 0; i < divs.length; i++)
    {
        var theDiv = divs[i];
        var tables = theDiv.getElementsByTagName("table");
        if (!tables) continue;
        for (var j = 0; j < tables.length; j++)
        {
            var theTable = tables[j];
            // If we found a sortable table that is asking for autosort...
            if ($(theTable).hasClass("sortable") && $(theTable).hasClass("autosort"))
            {
                var sortColumnNum = 1, curColumnNum = 0;

                // First check if there is any by-column-x class at all, then take the time to figure out the value of x
                var patt = /by-column/;
                if (patt.test(theTable.className))
                {
                    for (var col = 1; col < 10; col++)
                    {
                        var colOption = "by-column-" + col;
                        if ($(theTable).hasClass(colOption))
                        {
                            //console.log("Table " + j + " wants to sort by column " + col);
                            sortColumnNum = col;

                            // Now look for sort button and click it
                            var allTHs = theTable.getElementsByTagName("th");
                            if (!allTHs)
                            {
                                console.log("Failed to get 'th' elements!");
                                continue;
                            }
                            for (var k = 0; k < allTHs.length; k++)
                            {
                                //console.log("Class names for 'th' # " + k + " are " + allTHs[k].className);
                                if ($(allTHs[k]).hasClass("headerSort"))
                                {
                                    curColumnNum++;
                                    if (curColumnNum == sortColumnNum)
                                    {
                                        console.log("Clicking sort button (ID " + k + ") for column " + sortColumnNum);
                                        $(allTHs[k]).trigger("click"); // use jQuery's trigger() to send click event to this arrow
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
$(sortTimer);

/***** Auto-formatting dates ********
 * Re-formats a date placed by [[Template:LocaleDate]] to match the
 * month/day order of the user's locale.
 *
 *  Maintainers: [[User:Iritscen]]
 */
function getLang()
{
  if (navigator.languages !== undefined) 
    return navigator.languages[0]; 
  return navigator.language;
}
/* Because CSS variables didn't seem to work for changing the .date-[day/month]::before rules,
   this function manually trawls the stylesheets until it finds those rules and changes them */
function changeDateSeparator(component, separator)
{
  const styleSheets = document.styleSheets;
  var ruleIndex;
  
  for (var i = 0; i < styleSheets.length; i++)
  {
    const rules = styleSheets[i].cssRules || styleSheets[i].rules;
    for (var j = 0; j < rules.length; j++)
    {
      if (rules[j].selectorText === '.mw-parser-output .date-'+component+'::before')
      {
        ruleIndex = j;
        break;
      }
    }

    if (ruleIndex !== undefined)
    {
      // Update the content of the existing rule
      styleSheets[i].deleteRule(ruleIndex);
      styleSheets[i].insertRule('.date-'+component+'::before{content:"'+separator+'";}', ruleIndex);
      break;
    }
  }
}
function localizeDates()
{
  const lang = getLang();
  const root = document.querySelector(':root');

  if (root == undefined || lang == undefined)
    return;
 
  if (lang != "en-US") // Template:LocaleDate prints dates in U.S.-style by default
  {
    root.style.setProperty('--month-order', '2');
    root.style.setProperty('--day-order', '1');
    changeDateSeparator('month', '/');
    changeDateSeparator('day', '');
  }
}
$(localizeDates);