MediaWiki:Common.js: Difference between revisions

finished TODO item
(why does it only find the sort button some of the time?)
(finished TODO item)
 
(32 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* 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 **********
/** extract a URL parameter from the current URL **********
  * From [[en:User:Lupin/autoedit.js]]
  * From [[en:User:Lupin/autoedit.js]]
Line 14: Line 19:
     return null;
     return null;
}
}
 
/** &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 25: Line 30:
}
}


/* 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');


/* Import more specific scripts if necessary */
/**
if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit' || mw.config.get('wgPageName') === 'Special:Upload') // load script specific to editing and upload pages
* Collapsible tables; reimplemented with mw-collapsible
{
* Styling is also in place to avoid FOUC
    mw.loader.load('/w/index.php?title=MediaWiki:Common.js/edit.js&action=raw&ctype=text/javascript');
*
* 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();
      } );
  }
}
}
else if (mw.config.get('wgPageName') === 'Special:Search') // load script for augmenting search page
mw.hook( 'wikipage.content' ).add( makeCollapsibleMwCollapsible );
{
 
    mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript');
/**
* 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 */


/* Scripts specific to Internet Explorer */
/* Focus us on the Search box if we're on the Main Page, so we can immediately start typing */
if (navigator.appName == 'Microsoft Internet Explorer')
if (mw.config.get('wgIsMainPage'))
{
{
     /** Internet Explorer bug fix **************************************************
     $(function()
    *
    *  Description: Fixes IE horizontal scrollbar bug
    *  Maintainers: [[User:Tom-]]?
    */
   
    var oldWidth;
    var docEl = document.documentElement;
   
    var fixIEScroll = function()
     {
     {
         if (!oldWidth || docEl.clientWidth > oldWidth)
         $('#searchInput').focus();
        {
     });
            doFixIEScroll();
        }
        else
        {
            setTimeout(doFixIEScroll, 1);
        }
       
        oldWidth = docEl.clientWidth;
    };
   
    var doFixIEScroll = function()
    {
        docEl.style.overflowX = (docEl.scrollWidth - docEl.clientWidth < 4) ? "hidden" : "";
    };
   
    document.attachEvent("onreadystatechange", fixIEScroll);
    document.attachEvent("onresize", fixIEScroll);
   
    // In print IE (7?) does not like line-height
    appendCSS('@media print {sup, sub, p, .documentDescription {line-height: normal;}}');
 
    // IE overflow bug
    appendCSS('div.overflowbugx {overflow-x: scroll !important; overflow-y: hidden !important;} div.overflowbugy {overflow-y: scroll !important; overflow-x: hidden !important;}');
 
    // IE zoomfix
    // Use to fix right floating div/table inside tables
     appendCSS('.iezoomfix div, .iezoomfix table {zoom: 1;}');
}
}


/** Hover tables *********************************************************
/** Hover tables *********************************************************
Line 108: Line 140:
     }
     }
}
}
function initHoverTables()
function initHoverTables()
{
{
Line 130: Line 161:
     }
     }
}
}
$(initHoverTables);
$(initHoverTables);


Line 154: Line 184:
         gif_img.src = gif_src.replace('.gif', '.jpg');
         gif_img.src = gif_src.replace('.gif', '.jpg');
}
}
function initHoverGIFs()
function initHoverGIFs()
{
{
Line 168: Line 197:
     }
     }
}
}
$(initHoverGIFs);
$(initHoverGIFs);


 
/***** Auto-sorting tables ********
/** Collapsible tables *********************************************************
*
*  Description: Allows tables to be collapsed, showing only the header. See
*              [[Wikipedia:NavFrame]].
*  Maintainers: [[User:R. Koot]]
*/
 
var autoCollapse = 2;
var collapseCaption = "hide";
var expandCaption = "show";
 
function collapseTable(tableIndex)
{
    var Button = document.getElementById("collapseButton" + tableIndex);
    var Table = document.getElementById("collapsibleTable" + tableIndex);
 
    if (!Table || !Button)
    {
        return false;
    }
 
    var Rows = Table.rows;
 
    if (Button.firstChild.data == collapseCaption)
    {
        for (var i = 1; i < Rows.length; i++)
        {
            Rows[i].style.display = "none";
        }
        Button.firstChild.data = expandCaption;
    }
    else
    {
        for (var i = 1; i < Rows.length; i++)
        {
            Rows[i].style.display = Rows[0].style.display;
        }
        Button.firstChild.data = collapseCaption;
    }
}
 
function createCollapseButtons()
{
    var tableIndex = 0;
    var NavigationBoxes = new Object();
    var Tables = document.getElementsByTagName("table");
 
    for (var i = 0; i < Tables.length; i++)
    {
        if ($(Tables[i]).hasClass("collapsible"))
        {
            /* only add button and increment count if there is a header row to work with */
            var HeaderRow = Tables[i].getElementsByTagName("tr")[0];
            if (!HeaderRow) continue;
            var Header = HeaderRow.getElementsByTagName("th")[0];
            if (!Header) continue;
 
            NavigationBoxes[tableIndex] = Tables[i];
            Tables[i].setAttribute("id", "collapsibleTable" + tableIndex);
 
            var Button    = document.createElement("span");
            var ButtonLink = document.createElement("a");
            var ButtonText = document.createTextNode(collapseCaption);
 
            Button.className = "collapseButton"; // styles are declared in Common.css
 
            ButtonLink.style.color = Header.style.color;
            ButtonLink.setAttribute("id", "collapseButton" + tableIndex);
            ButtonLink.setAttribute("href", "#");
            $(ButtonLink).click(new Function("evt", "collapseTable(" + tableIndex + " ); return killEvt(evt);"));
            ButtonLink.appendChild(ButtonText);
 
            Button.appendChild(document.createTextNode("["));
            Button.appendChild(ButtonLink);
            Button.appendChild(document.createTextNode("]"));
 
            Header.insertBefore(Button, Header.childNodes[0]);
            tableIndex++;
        }
    }
 
    for (var i = 0; i < tableIndex; i++)
    {
        if ($(NavigationBoxes[i]).hasClass("collapsed") || (tableIndex >= autoCollapse && $(NavigationBoxes[i]).hasClass("autocollapse")))
        {
            collapseTable(i);
        }
        else if ($(NavigationBoxes[i]).hasClass("innercollapse"))
        {
            var element = NavigationBoxes[i];
            while (element = element.parentNode)
            {
                if ($(element).hasClass("outercollapse"))
                {
                    collapseTable(i);
                    break;
                }
            }
        }
    }
}
 
$(createCollapseButtons);
 
 
/** Dynamic Navigation Bars (experimental) *************************************
*
*  Description: See [[Wikipedia:NavFrame]].
*  Maintainers: UNMAINTAINED
*/
 
// set up the words in your language
var NavigationBarHide = '[' + collapseCaption + ']';
var NavigationBarShow = '[' + expandCaption + ']';
 
// shows and hides content and picture (if available) of navigation bars
// Parameters:
//    indexNavigationBar: the index of navigation bar to be toggled
function toggleNavigationBar(indexNavigationBar)
{
    var NavToggle = document.getElementById("NavToggle" + indexNavigationBar);
    var NavFrame = document.getElementById("NavFrame" + indexNavigationBar);
 
    if (!NavFrame || !NavToggle)
        return false;
 
    // if shown now
    if (NavToggle.firstChild.data == NavigationBarHide)
    {
        for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling)
        {
            if ($(NavChild).hasClass("NavContent") || $(NavChild).hasClass("NavPic"))
            {
                NavChild.style.display = 'none';
            }
        }
    NavToggle.firstChild.data = NavigationBarShow;
 
    // if hidden now
    }
    else if (NavToggle.firstChild.data == NavigationBarShow)
    {
        for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling)
        {
            if ($(NavChild).hasClass("NavContent") || $(NavChild).hasClass("NavPic"))
            {
                NavChild.style.display = 'block';
            }
        }
        NavToggle.firstChild.data = NavigationBarHide;
    }
}
 
// adds show/hide-button to navigation bars
function createNavigationBarToggleButton()
{
    var indexNavigationBar = 0;
    // iterate over all < div >-elements
    var divs = document.getElementsByTagName("div");
    for (var i = 0; NavFrame = divs[i]; i++)
    {
        // if found a navigation bar
        if ($(NavFrame).hasClass("NavFrame"))
        {
            indexNavigationBar++;
            var NavToggle = document.createElement("a");
            NavToggle.className = 'NavToggle';
            NavToggle.setAttribute('id', 'NavToggle' + indexNavigationBar);
            NavToggle.setAttribute('href', 'javascript:toggleNavigationBar(' + indexNavigationBar + ');');
 
            var isCollapsed = $(NavFrame).hasClass("collapsed");
            /*
            * Check if any children are already hidden.  This loop is here for backwards compatibility:
            * the old way of making NavFrames start out collapsed was to manually add style="display:none"
            * to all the NavPic/NavContent elements.  Since this was bad for accessibility (no way to make
            * the content visible without JavaScript support), the new recommended way is to add the class
            * "collapsed" to the NavFrame itself, just like with collapsible tables.
            */
            for (var NavChild = NavFrame.firstChild; NavChild != null && !isCollapsed; NavChild = NavChild.nextSibling)
            {
                if ($(NavChild).hasClass("NavPic") || $(NavChild).hasClass("NavContent"))
                {
                    if (NavChild.style.display == 'none')
                    {
                        isCollapsed = true;
                    }
                }
            }
            if (isCollapsed)
            {
                for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling)
                {
                    if ($(NavChild).hasClass("NavPic") || $(NavChild).hasClass("NavContent"))
                    {
                        NavChild.style.display = 'none';
                    }
                }
            }
            var NavToggleText = document.createTextNode(isCollapsed ? NavigationBarShow : NavigationBarHide);
            NavToggle.appendChild(NavToggleText);
 
            // Find the NavHead and attach the toggle link (Must be this complicated because Moz's firstChild handling is borked)
            for (var j = 0; j < NavFrame.childNodes.length; j++)
            {
                if ($(NavFrame.childNodes[j]).hasClass("NavHead"))
                {
                    NavToggle.style.color = NavFrame.childNodes[j].style.color;
                    NavFrame.childNodes[j].appendChild(NavToggle);
                }
            }
            NavFrame.setAttribute('id', 'NavFrame' + indexNavigationBar);
        }
    }
}
 
$(createNavigationBarToggleButton);
 
 
// Allows sidebar's "navigation", "toolbox" and "languages" sections to be customized
function ModifySidebar(action, section, name, link)
{
    try
    {
        switch (section)
        {
          case "languages":
            var target = "p-lang";
            break;
          case "toolbox":
            var target = "p-tb";
            break;
          case "navigation":
            var target = "p-navigation";
            break;
          default:
            var target = "p-" + section;
            break;
        }
        if (action == "add")
        {
            var node = document.getElementById(target)
                              .getElementsByTagName('div')[0]
                              .getElementsByTagName('ul')[0];
            var aNode = document.createElement('a');
            var liNode = document.createElement('li');
            aNode.appendChild(document.createTextNode(name));
            aNode.setAttribute('href', link);
            liNode.appendChild(aNode);
            liNode.className='plainlinks';
            node.appendChild(liNode);
        }
 
        if (action == "sep")
        {
            var node = document.getElementById(target)
                              .getElementsByTagName('div')[0]
                              .getElementsByTagName('ul')[0];
            var liNode = document.createElement('li');
            liNode.style.listStyleImage="url('http://wiki.oni2.net/w/images/1/10/Separator.png')";
            liNode.style.listStylePosition='inside';
            node.appendChild(liNode);
        }
        if (action == "remove")
        {
            var list = document.getElementById(target)
                              .getElementsByTagName('div')[0]
                              .getElementsByTagName('ul')[0];
            var listelements = list.getElementsByTagName('li');
            for (var i = 0; i < listelements.length; i++)
            {
                if (listelements[i].getElementsByTagName('a')[0].innerHTML == name ||
                    listelements[i].getElementsByTagName('a')[0].href == link)
                {
                    list.removeChild(listelements[i]);
                }
            }
        }
    }
    catch(e)
    {
      // let's just ignore what's happened
      return;
    }
}
 
 
/** Table sorting fixes ************************************************
  *
  *  Description: Disables code in table sorting routine to set classes on even/odd rows
  *  Maintainers: [[User:Random832]]
  */
ts_alternate_row_colors = false;
 
 
/***** uploadwizard_newusers ********
* Switches in a message for non-autoconfirmed users at [[Wikipedia:Upload]]
*
*  Maintainers: [[User:Krimpet]]
*/
function uploadwizard_newusers()
{
    if (mw.config.get('wgNamespaceNumber') == 4 && mw.config.get('wgTitle') == 'Upload' && mw.config.get('wgAction') === 'view')
    {
        var oldDiv = document.getElementById("autoconfirmedusers"),
            newDiv = document.getElementById("newusers");
        if (oldDiv && newDiv)
        {
            if (typeof wgUserGroups == "object" && wgUserGroups)
            {
                for (i = 0; i < wgUserGroups.length; i++)
                {
                    if (wgUserGroups[i] == "autoconfirmed")
                    {
                        oldDiv.style.display = "block";
                        newDiv.style.display = "none";
                        return;
                    }
                }
            }
            oldDiv.style.display = "none";
            newDiv.style.display = "block";
            return;
        }
    }
}
 
$(uploadwizard_newusers);
 
 
/***** autosort_sortable ********
  * 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 517: Line 206:
  *  Maintainers: [[User:Iritscen]]
  *  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()
function sortSortableTables()
{
{
    console.log("Sorting the autosort tables.");
     // Iterate over all <div> elements
     // Iterate over all <div> elements
     var divs = document.getElementsByTagName("div");
     var divs = document.getElementsByTagName("div");
Line 545: Line 242:
                         if ($(theTable).hasClass(colOption))
                         if ($(theTable).hasClass(colOption))
                         {
                         {
                             alert("Table " + j " wants to sort by column " + sortColumnNum);
                             //console.log("Table " + j + " wants to sort by column " + col);
                             sortColumnNum = col;
                             sortColumnNum = col;
                            break;
                        }
                    }
                }


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


$(sortSortableTables);
/***** 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);