This is a wiki for a reason. Anyone can contribute. If you see something that is inaccurate or can be improved, don't ask that it be fixed--just improve it.
[ Disclaimer, Create new user --- Wiki markup help, Install P99 ]

Difference between revisions of "MediaWiki:Common.js"

From Project 1999 Wiki
Jump to: navigation, search
Line 9: Line 9:
  
 
   if (onHttp && haveNotTriedAlready) {
 
   if (onHttp && haveNotTriedAlready) {
     location = location.origin.replace('http:', 'https:') +  
+
     location =
              location.pathname +  
+
      location.origin.replace('http:', 'https:') +
              location.search +  
+
      location.pathname +
                (location.search[0] === '?' ? '&' : '?') +
+
      location.search +
                'redirected_from_http=1' +
+
      (location.search[0] === '?' ? '&' : '?') +
              location.hash
+
      'redirected_from_http=1' +
 +
      location.hash;
 
   }
 
   }
 
} catch (err) {
 
} catch (err) {
Line 25: Line 26:
  
 
// Get all magelos of current user
 
// Get all magelos of current user
// (currently un-used, but intended for future achievement code, so that  
+
// (currently un-used, but intended for future achievement code, so that
//  we can have an "add this achievement to my magelo" drop-down with all  
+
//  we can have an "add this achievement to my magelo" drop-down with all
 
//  their magelos listed)
 
//  their magelos listed)
// var getMagelosForCurrentUser = function() {
+
var getMagelosForCurrentUser = function () {
//   var userName = $('#pt-userpage a').text();
+
   var userName = $('#pt-userpage a').text();
//   fetch(`https://wiki.project1999.com/index.php?limit=50&tagfilter=&title=Special%3AContributions&contribs=user&target=${userName}&namespace=500&topOnly=1&year=&month=-1`)
+
   var url =
//     .then(function(res) { return res.text(); })
+
    'https://wiki.project1999.com/index.php?limit=50&tagfilter=&' +
//     .then(function(html) {
+
    'title=Special%3AContributions&contribs=user&target=' +
//       var mageloLinks = Array.from($(html).find('li a:contains("Magelo")'));
+
    userName +
//       var names = mageloLinks.filter(function(i,a) {
+
    '&namespace=500&topOnly=1&year=&month=-1';
//        !$(a).text().includes('(')).map((i, el) => $(el).text()  
+
  fetch(url)
//      })
+
     .then(function (response) {
//       console.log(names);
+
      return response.text();
//     })
+
    })
// };
+
     .then(function (html) {
 
+
       var mageloLinks = Array.from($(html).find('li a:contains("Magelo")'));
 +
       var names = mageloLinks
 +
        .filter(function (i, a) {
 +
          return !$(a).text().includes('(');
 +
        })
 +
        .map(function (i, el) {
 +
          return $(el).text();
 +
        });
 +
       console.log(names);
 +
     });
 +
};
  
 
// **********************************
 
// **********************************
Line 47: Line 58:
  
 
/**
 
/**
  * Basic "state getter": checks localStorage for *stateKey*, then return the  
+
  * Basic "state getter": checks localStorage for *stateKey*, then return the
 
  * value for *pageName* (if any) found on that object.
 
  * value for *pageName* (if any) found on that object.
 
  */
 
  */
var getPageState = function(pageName, stateKey) {
+
var getPageState = function (pageName, stateKey) {
 
   // Get the state for all pages for this key
 
   // Get the state for all pages for this key
 
   var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');
 
   var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');
Line 63: Line 74:
 
  * status state for the current page.
 
  * status state for the current page.
 
  */
 
  */
window.getStateByPage = function(stateKey) {
+
window.getStateByPage = function (stateKey) {
 
   var pageName = location.pathname.substr(1); // ditch the leading "/"
 
   var pageName = location.pathname.substr(1); // ditch the leading "/"
 
   return getPageState(pageName, stateKey);
 
   return getPageState(pageName, stateKey);
Line 73: Line 84:
 
  * about which page you are on)
 
  * about which page you are on)
 
  */
 
  */
window.getGlobalState = function(stateKey) {
+
window.getGlobalState = function (stateKey) {
 
   return getPageState('__global', stateKey);
 
   return getPageState('__global', stateKey);
 
};
 
};
Line 80: Line 91:
 
  * Basic "state setter" function
 
  * Basic "state setter" function
 
  */
 
  */
var setPageState = function(pageName, stateKey, state) {
+
var setPageState = function (pageName, stateKey, state) {
 
   // Get the state for this key (for all pages) out of local storage
 
   // Get the state for this key (for all pages) out of local storage
 
   var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');
 
   var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');
Line 97: Line 108:
 
  * value of "b".
 
  * value of "b".
 
  */
 
  */
window.setStateByPage = function(stateKey, state) {
+
window.setStateByPage = function (stateKey, state) {
 
   var pageName = location.pathname.substr(1); // ditch the leading "/"
 
   var pageName = location.pathname.substr(1); // ditch the leading "/"
 
   setPageState(pageName, stateKey, state);
 
   setPageState(pageName, stateKey, state);
 
};
 
};
  
window.setGlobalState = function(stateKey, state) {
+
window.setGlobalState = function (stateKey, state) {
 
   return setPageState('__global', stateKey, state);
 
   return setPageState('__global', stateKey, state);
 
};
 
};
 
  
 
// *******************************
 
// *******************************
Line 113: Line 123:
 
  * last update: 7 January, 2018 by Loramin
 
  * last update: 7 January, 2018 by Loramin
 
  */
 
  */
$(function() {
+
$(function () {
 
   var hideDelay = 0;
 
   var hideDelay = 0;
 
   var trigDelay = 250;
 
   var trigDelay = 250;
Line 122: Line 132:
  
 
   // One instance that's reused to show info for the current person
 
   // One instance that's reused to show info for the current person
   var container = $('<div id="itemHoverContainer">'
+
   var container = $(
    + '<div id="HoverContent"></div>'
+
    '<div id="itemHoverContainer">' + '<div id="HoverContent"></div>' + '</div>'
    + '</div>');
+
  );
  
 
   $('body').append(container);
 
   $('body').append(container);
  
/* --- hoverbox for item/mob, currently only used in magelo --- */
+
  /* --- hoverbox for item/mob, currently only used in magelo --- */
  
 
   // Determine which "a" elements should trigger the item stats mouseover
 
   // Determine which "a" elements should trigger the item stats mouseover
 
   var $mouseoverTargets = $('span.ih a');
 
   var $mouseoverTargets = $('span.ih a');
   var isItemCategory = document.title.startsWith('Category:') && document.title.includes('Equipment - Project 1999 Wiki') &&
+
   var isItemCategory =
                      !document.title.includes('Worshiper Equipment');
+
    document.title.startsWith('Category:') &&
 +
    document.title.includes('Equipment - Project 1999 Wiki') &&
 +
    !document.title.includes('Worshiper Equipment');
 
   if (isItemCategory) {
 
   if (isItemCategory) {
 
     // Include the category's item list along with ".ih" links
 
     // Include the category's item list along with ".ih" links
Line 139: Line 151:
 
   }
 
   }
  
   $mouseoverTargets = $mouseoverTargets.filter(function(i, a) {
+
   $mouseoverTargets = $mouseoverTargets.filter(function (i, a) {
 
     // Don't add hover to links like "next 200"
 
     // Don't add hover to links like "next 200"
     return !$(a).attr('href').startsWith('/Special:') &&
+
     return (
          !$(a).attr('href').includes('title=Category:')
+
      !$(a).attr('href').startsWith('/Special:') &&
 +
      !$(a).attr('href').includes('title=Category:')
 +
    );
 
   });
 
   });
  
   $mouseoverTargets.on('mouseover', function()
+
   $mouseoverTargets.on('mouseover', function () {
  {
+
 
     var $this = $(this);
 
     var $this = $(this);
 
     var itemname = $this.attr('title');
 
     var itemname = $this.attr('title');
  
     if (itemname == '' || itemname == 'undefined')
+
     if (itemname == '' || itemname == 'undefined') return;
      return;
+
  
     if (hideTimer)
+
     if (hideTimer) clearTimeout(hideTimer);
      clearTimeout(hideTimer);
+
  
 
     if ($this.parents('div.mw-content-ltr').length) {
 
     if ($this.parents('div.mw-content-ltr').length) {
Line 161: Line 172:
  
 
       container.css({
 
       container.css({
          left: pos.left + 5 + 'px',
+
        left: pos.left + 5 + 'px',
          top: pos.top + 5 + 'px'
+
        top: pos.top + 5 + 'px',
 
       });
 
       });
 
     }
 
     }
 
 
  
 
     $(this).trigger('mousemove');
 
     $(this).trigger('mousemove');
Line 177: Line 186:
 
     //                          + '<p></p></div></div><div class="itembotbg"></div>');
 
     //                          + '<p></p></div></div><div class="itembotbg"></div>');
  
     if (ajax)
+
     if (ajax) {
    {
+
 
       ajax.abort();
 
       ajax.abort();
 
       ajax = null;
 
       ajax = null;
 
     }
 
     }
 
     ajax = $.ajax({
 
     ajax = $.ajax({
       url: window.location.protocol +
+
       url:
          '//wiki.project1999.com/index.php/Special:AjaxHoverHelper/' + itemname,
+
        window.location.protocol +
 +
        '//wiki.project1999.com/index.php/Special:AjaxHoverHelper/' +
 +
        itemname,
 
       cacheResponse: true,
 
       cacheResponse: true,
       success: function(html)
+
       success: function (html) {
      {
+
 
         var $html = $(html);
 
         var $html = $(html);
         $('#itemHoverContent').html($html[2]).prepend($html[0]).prepend($html[1]);
+
         $('#itemHoverContent')
       }
+
          .html($html[2])
 +
          .prepend($html[0])
 +
          .prepend($html[1]);
 +
       },
 
     });
 
     });
  
 
     container.css('display', 'block');
 
     container.css('display', 'block');
 
     //container.fadeIn('fast');
 
     //container.fadeIn('fast');
 
 
   }); //on mouseover
 
   }); //on mouseover
  
   $('span.ih a').on('mouseout', function()
+
   $('span.ih a').on('mouseout', function () {
  {
+
     if (hideTimer) clearTimeout(hideTimer);
     if (hideTimer)
+
     hideTimer = setTimeout(function () {
      clearTimeout(hideTimer);
+
     hideTimer = setTimeout(function()
+
    {
+
 
       container.css('display', 'none');
 
       container.css('display', 'none');
 
       //container.fadeOut('fast');
 
       //container.fadeOut('fast');
Line 209: Line 217:
 
   });
 
   });
  
   $('span.ih a').mousemove(function(e){
+
   $('span.ih a').mousemove(function (e) {
 
     var mousex = e.pageX + 20; //Get X coodrinates
 
     var mousex = e.pageX + 20; //Get X coodrinates
 
     var mousey = e.pageY + 20; //Get Y coordinates
 
     var mousey = e.pageY + 20; //Get Y coordinates
Line 220: Line 228:
 
     var tipVisY = $(window).height() - (mousey + tipHeight);
 
     var tipVisY = $(window).height() - (mousey + tipHeight);
  
     if ( tipVisX < 20 ) { //If tooltip exceeds the X coordinate of viewport
+
     if (tipVisX < 20) {
 +
      //If tooltip exceeds the X coordinate of viewport
  
       if( tipWidth > e.pageX - 20 ){
+
       if (tipWidth > e.pageX - 20) {
 
         mousex = 0;
 
         mousex = 0;
 
       } else {
 
       } else {
 
         mousex = e.pageX - tipWidth - 20;
 
         mousex = e.pageX - tipWidth - 20;
 
       }
 
       }
 
+
     }
     } if ( tipVisY < 20 ) { //If tooltip exceeds the Y coordinate of viewport
+
    if (tipVisY < 20) {
 +
      //If tooltip exceeds the Y coordinate of viewport
 
       mousey = e.pageY - tipHeight - 20;
 
       mousey = e.pageY - tipHeight - 20;
 
     }
 
     }
  
     container.css({ top: mousey, left: mousex });
+
     container.css({ top: mousey, left: mousex });
 
   });
 
   });
  
 
   // Allow mouse over of details without hiding details
 
   // Allow mouse over of details without hiding details
   $('#itemHoverContainer').mouseover(function()
+
   $('#itemHoverContainer').mouseover(function () {
  {
+
     if (hideTimer) clearTimeout(hideTimer);
     if (hideTimer)
+
      clearTimeout(hideTimer);
+
 
   });
 
   });
  
 
   // Hide after mouseout
 
   // Hide after mouseout
   $('#itemHoverContainer').mouseout(function()
+
   $('#itemHoverContainer').mouseout(function () {
  {
+
     if (hideTimer) clearTimeout(hideTimer);
     if (hideTimer)
+
     hideTimer = setTimeout(function () {
      clearTimeout(hideTimer);
+
     hideTimer = setTimeout(function()
+
    {
+
 
       container.css('display', 'none');
 
       container.css('display', 'none');
 
       //container.fadeOut('fast');
 
       //container.fadeOut('fast');
Line 255: Line 260:
  
 
   // magelo non-ajax item hover, but move box with mouse
 
   // magelo non-ajax item hover, but move box with mouse
   $('.magelohb').mousemove(function(e){
+
   $('.magelohb').mousemove(function (e) {
 
     var childContainer = $(this).children('span.hb');
 
     var childContainer = $(this).children('span.hb');
  
Line 269: Line 274:
 
     var tipVisY = $(window).height() - (mousey + tipHeight - 20);
 
     var tipVisY = $(window).height() - (mousey + tipHeight - 20);
  
     if ( tipVisX < 20 ) { //If tooltip exceeds the X coordinate of viewport
+
     if (tipVisX < 20) {
 +
      //If tooltip exceeds the X coordinate of viewport
  
       if( tipWidth > e.pageX - 20){
+
       if (tipWidth > e.pageX - 20) {
 
         mousex = 0;
 
         mousex = 0;
 
       } else {
 
       } else {
 
         mousex = e.pageX - tipWidth - 20;
 
         mousex = e.pageX - tipWidth - 20;
 
       }
 
       }
 
+
     }
     } if ( tipVisY < 20 ) { //If tooltip exceeds the Y coordinate of viewport
+
    if (tipVisY < 20) {
 +
      //If tooltip exceeds the Y coordinate of viewport
 
       mousey = e.pageY - tipHeight - 20;
 
       mousey = e.pageY - tipHeight - 20;
 
     }
 
     }
  
     childContainer.css({ top: mousey, left: mousex, 'z-index':'999' });
+
     childContainer.css({ top: mousey, left: mousex, 'z-index': '999' });
 
   });
 
   });
  
 
   // change to position:fixed on all hover divs if we have JS active
 
   // change to position:fixed on all hover divs if we have JS active
 
   // otherwise leave as position:absolute so the stationary hovers are near their items
 
   // otherwise leave as position:absolute so the stationary hovers are near their items
   $('.magelohb span.hb').each(function(i) {
+
   $('.magelohb span.hb').each(function (i) {
     $(this).css({'position':'fixed'});
+
     $(this).css({ position: 'fixed' });
 
   });
 
   });
  
Line 297: Line 304:
  
 
   // Fashion for item pages
 
   // Fashion for item pages
   var extractFashionHtml = function(html) {
+
   var extractFashionHtml = function (html) {
     return $(html).find('.fashion_show, .primary_secondary_show').map(function(i, el) {
+
     return $(html)
      var $el = $(el);
+
      .find('.fashion_show, .primary_secondary_show')
      var data = $el.data();
+
      .map(function (i, el) {
      data.race = $el.find('.fashion_race').html();
+
        var $el = $(el);
      if ($el.is('.fashion_show')) {
+
        var data = $el.data();
        data.armor = $el.find('.fashion_armor').html();
+
        data.race = $el.find('.fashion_race').html();
      }
+
        if ($el.is('.fashion_show')) {
      return data;
+
          data.armor = $el.find('.fashion_armor').html();
    }).toArray();
+
        }
  };
+
        return data;
 
+
      })
   var getFashionShows = function(fashionCategory) {
+
      .toArray();
 +
  };
 +
 
 +
   var getFashionShows = function (fashionCategory) {
 
     var url = '/Category:Fashion: ' + fashionCategory;
 
     var url = '/Category:Fashion: ' + fashionCategory;
 
     return $.get(url).then(extractFashionHtml);
 
     return $.get(url).then(extractFashionHtml);
 
   };
 
   };
  
   var getItemPageShows = function() {
+
   var getItemPageShows = function () {
     var fashionCategories =
+
     var fashionCategories = $('#catlinks li a')
      $('#catlinks li a')
+
      .map(function (i, a) {
        .map(function(i, a) { return $(a).text(); })
+
        return $(a).text();
        .filter(function(i, text) { return text.startsWith('Fashion:'); })
+
      })
        .map(function(i, text) { return text.substr('Fashion: '.length); });
+
      .filter(function (i, text) {
 +
        return text.startsWith('Fashion:');
 +
      })
 +
      .map(function (i, text) {
 +
        return text.substr('Fashion: '.length);
 +
      });
 
     var fashion = fashionCategories[0];
 
     var fashion = fashionCategories[0];
 
     if (!fashion) return $.when();
 
     if (!fashion) return $.when();
  
 
     var tint = fashionCategories[1];
 
     var tint = fashionCategories[1];
     return getFashionShows(fashion).then(function(shows) {
+
     return getFashionShows(fashion).then(function (shows) {
 
       var sameTint = [];
 
       var sameTint = [];
 
       var otherTint = [];
 
       var otherTint = [];
       shows.forEach(function(show) {
+
       shows.forEach(function (show) {
 
         var isHeld = show.primaryFashion || show.secondaryFashion;
 
         var isHeld = show.primaryFashion || show.secondaryFashion;
 
         var bothHaveSameTint = show.tint === tint;
 
         var bothHaveSameTint = show.tint === tint;
Line 334: Line 349:
 
         else otherTint.push(show);
 
         else otherTint.push(show);
 
       });
 
       });
       return { matches: sameTint, partialMatches: otherTint, fashion: fashion, tint: tint };
+
       return {
 +
        matches: sameTint,
 +
        partialMatches: otherTint,
 +
        fashion: fashion,
 +
        tint: tint,
 +
      };
 
     });
 
     });
   }
+
   };
   var addFashionSection = function(shows, isFullMatch) {
+
   var addFashionSection = function (shows, isFullMatch) {
 
     var preface = isFullMatch
 
     var preface = isFullMatch
              ? '<h2>Fashion/Appearance</h2>'  
+
      ? '<h2>Fashion/Appearance</h2>'
              : '<div>Fashion images with the wrong "tint" (i.e. coloring) which ' +
+
      : '<div>Fashion images with the wrong "tint" (i.e. coloring) which ' +
                'can only be used to get a general sense of the item\'s ' +
+
        "can only be used to get a general sense of the item's " +
                'appearance:</div>';
+
        'appearance:</div>';
  
 
     // Don't show the explanatory text if there are no shows to explain
 
     // Don't show the explanatory text if there are no shows to explain
 
     if (!shows.length && !isFullMatch) preface = '';
 
     if (!shows.length && !isFullMatch) preface = '';
  
     var $ul = $('<ul>').append(shows.map(function(show) {
+
     var $ul = $('<ul>').append(
      var img = isFullMatch
+
      shows.map(function (show) {
        ? '<br/><img style="max-height: 100px; max-width: 100px;" src="/images/' +
+
        var img = isFullMatch
          show.file.replace(/ /g, '_') + '"/>'
+
          ? '<br/><img style="max-height: 100px; max-width: 100px;" src="/images/' +
        : '';
+
            show.file.replace(/ /g, '_') +
      return '<li class="fashion-link" data-file="' + show.file +'">' +
+
            '"/>'
            '<a href="#">' + show.gender + ' ' + show.race + img + '</a></li>';
+
          : '';
    }));
+
        return (
 +
          '<li class="fashion-link" data-file="' +
 +
          show.file +
 +
          '">' +
 +
          '<a href="#">' +
 +
          show.gender +
 +
          ' ' +
 +
          show.race +
 +
          img +
 +
          '</a></li>'
 +
        );
 +
      })
 +
    );
  
 
     $('#itemfashion').before($('<div>' + preface + '</div>').append($ul));
 
     $('#itemfashion').before($('<div>' + preface + '</div>').append($ul));
 
   };
 
   };
  
   var addFashionSections = function() {
+
   var addFashionSections = function () {
     getItemPageShows().then(function(pageShows) {
+
     getItemPageShows().then(function (pageShows) {
       if(!pageShows) return;
+
       if (!pageShows) return;
 
       var matches = pageShows.matches || [];
 
       var matches = pageShows.matches || [];
 
       var partialMatches = pageShows.partialMatches || [];
 
       var partialMatches = pageShows.partialMatches || [];
 
       if (pageShows.matches.length || pageShows.partialMatches.length)
 
       if (pageShows.matches.length || pageShows.partialMatches.length)
 
         addFashionSection(pageShows.matches, true);
 
         addFashionSection(pageShows.matches, true);
       if (pageShows.partialMatches.length)  
+
       if (pageShows.partialMatches.length)
 
         addFashionSection(pageShows.partialMatches, false);
 
         addFashionSection(pageShows.partialMatches, false);
 
     });
 
     });
 
   };
 
   };
 
   addFashionSections();
 
   addFashionSections();
 
+
 
 
+
 
   //*****************************************
 
   //*****************************************
 
   // Add class-based filtering to item tables
 
   // Add class-based filtering to item tables
 
   //*****************************************w
 
   //*****************************************w
 
+
 
   // Determine whether or not a provided row should be shown for the provided  
+
   // Determine whether or not a provided row should be shown for the provided
 
   // class abbreviation (eg. "BRD")
 
   // class abbreviation (eg. "BRD")
   var isRowShown = function($tr, classAbbrev) {
+
   var isRowShown = function ($tr, classAbbrev) {
 
     // Extract classes (and races)
 
     // Extract classes (and races)
     var classText = $tr.find('td').eq(3).text().split('Class:')[1];
+
     var classText = $tr.find('td').eq(3).text().split('Class:')[1];
     if (!classText) return true; // Ignore (don't filter) rows without class text
+
     if (!classText) return true; // Ignore (don't filter) rows without class text
 
+
 
 
     // remove "Race: " part (if any) and upper-case text
 
     // remove "Race: " part (if any) and upper-case text
 
     classText = classText.split('Race:')[0] || classText;
 
     classText = classText.split('Race:')[0] || classText;
 
     classText = classText.toUpperCase();
 
     classText = classText.toUpperCase();
 
+
 
 
     // determine matching text
 
     // determine matching text
 
     var isMatch = classText.includes(classAbbrev);
 
     var isMatch = classText.includes(classAbbrev);
 
     var isAllMatch = classText.includes('ALL');
 
     var isAllMatch = classText.includes('ALL');
 
     var isExcept = classText.includes('ALL EXCEPT');
 
     var isExcept = classText.includes('ALL EXCEPT');
 
+
 
 
     // determine matches
 
     // determine matches
 
     if (isMatch && !isExcept) return true; // (eg. BRD in "BRD")
 
     if (isMatch && !isExcept) return true; // (eg. BRD in "BRD")
     if (!isMatch && isExcept) return true; // (eg. BRD in "ALL EXCEPT WIZ")
+
     if (!isMatch && isExcept) return true; // (eg. BRD in "ALL EXCEPT WIZ")
 
     if (!isExcept && isAllMatch) return true; // (eg. BRD in "ALL")
 
     if (!isExcept && isAllMatch) return true; // (eg. BRD in "ALL")
 
     return false;
 
     return false;
 
   };
 
   };
 
+
 
   var scrollToTable = function($table) {
+
   var scrollToTable = function ($table) {
     window.setTimeout(function() {
+
     window.setTimeout(function () {
       $('html, body').animate({ scrollTop: ($table.offset().top) }, 200);
+
       $('html, body').animate({ scrollTop: $table.offset().top }, 200);
 
     }, 100);
 
     }, 100);
 
   };
 
   };
 
+
 
   // Filter the provided table to only display rows for the provided class  
+
   // Filter the provided table to only display rows for the provided class
 
   // abbreviation
 
   // abbreviation
   var filterTable = function($table, classAbbrev) {
+
   var filterTable = function ($table, classAbbrev) {
 
     $table.find('tr').each(function (i, tr) {
 
     $table.find('tr').each(function (i, tr) {
 
       var $tr = $(tr);
 
       var $tr = $(tr);
Line 415: Line 446:
 
     });
 
     });
 
     scrollToTable($table);
 
     scrollToTable($table);
 
+
 
 
     addFilterLink($table, false);
 
     addFilterLink($table, false);
     $('.itemsUnfilterLink').click(function() {
+
     $('.itemsUnfilterLink').click(function () {
 
       unfilterTable($table);
 
       unfilterTable($table);
 
     });
 
     });
 
+
 
 
     alert('Filtered to only show rows containing ' + classAbbrev + ' gear.');
 
     alert('Filtered to only show rows containing ' + classAbbrev + ' gear.');
 
   };
 
   };
 
+
 
 
   // Restore a table to its previous, un-filtered state
 
   // Restore a table to its previous, un-filtered state
   var unfilterTable = function($table) {
+
   var unfilterTable = function ($table) {
 
     $table.find('tr').show();
 
     $table.find('tr').show();
 
     addFilterLink($table, true);
 
     addFilterLink($table, true);
 
     scrollToTable($table);
 
     scrollToTable($table);
 
   };
 
   };
 
+
 
   // When a user clicks on a table class filter link, handle it by asking them  
+
   // When a user clicks on a table class filter link, handle it by asking them
 
   // which class (and then filtering the table for that class)
 
   // which class (and then filtering the table for that class)
   var handleItemFilterLinkClick = function(e) {
+
   var handleItemFilterLinkClick = function (e) {
     var promptMessage = 'Please enter the three-letter abbreviation for the ' +
+
     var promptMessage =
        'class you want to filter by (eg. "brd" for "Bard").';
+
      'Please enter the three-letter abbreviation for the ' +
 
+
      'class you want to filter by (eg. "brd" for "Bard").';
 +
 
 
     var $table = $(e.target).closest('table');
 
     var $table = $(e.target).closest('table');
 
     var classAbbrev = prompt(promptMessage).toUpperCase();
 
     var classAbbrev = prompt(promptMessage).toUpperCase();
 
     if (!classAbbrev || classAbbrev.length !== 3 || classAbbrev === 'ALL') {
 
     if (!classAbbrev || classAbbrev.length !== 3 || classAbbrev === 'ALL') {
       alert('A 3-digit abbreviation wasn\'t entered (or "All" was entered); ' +
+
       alert(
           'filtering disabled');
+
        'A 3-digit abbreviation wasn\'t entered (or "All" was entered); ' +
 +
           'filtering disabled'
 +
      );
 
       unfilterTable($table);
 
       unfilterTable($table);
 
       return false;
 
       return false;
Line 447: Line 481:
 
     filterTable($table, classAbbrev);
 
     filterTable($table, classAbbrev);
 
   };
 
   };
 
+
 
 
   // Adds either a filter or unfilter link (as determined by showFilter) to the
 
   // Adds either a filter or unfilter link (as determined by showFilter) to the
 
   // provided table
 
   // provided table
   var addFilterLink = function($table, showFilter) {
+
   var addFilterLink = function ($table, showFilter) {
 
     var $statsHeaderCells = $table.find('th:contains("Stats")');
 
     var $statsHeaderCells = $table.find('th:contains("Stats")');
 
     var filterType = showFilter ? 'Filter' : 'Unfilter';
 
     var filterType = showFilter ? 'Filter' : 'Unfilter';
     $statsHeaderCells.html('Stats <span style="float:right">' +
+
     $statsHeaderCells.html(
         '<a style="text-decoration: underline" class="items' + filterType + 'Link" href="#">' +
+
      'Stats <span style="float:right">' +
         filterType + (showFilter ? ' by Class' : '') + '</a></span>');
+
         '<a style="text-decoration: underline" class="items' +
 
+
        filterType +
 +
        'Link" href="#">' +
 +
         filterType +
 +
        (showFilter ? ' by Class' : '') +
 +
        '</a></span>'
 +
    );
 +
 
 
     var $link = $statsHeaderCells.find('.items' + filterType + 'Link');
 
     var $link = $statsHeaderCells.find('.items' + filterType + 'Link');
 
     if (showFilter) $link.click(handleItemFilterLinkClick);
 
     if (showFilter) $link.click(handleItemFilterLinkClick);
     else $link.click(function() { unfilterTable($table); })
+
     else
 +
      $link.click(function () {
 +
        unfilterTable($table);
 +
      });
 
   };
 
   };
 
+
 
 
   // Add class filtering links to all item tables
 
   // Add class filtering links to all item tables
   var addItemFilteringLinks = function() {
+
   var addItemFilteringLinks = function () {
 
     var $tables = $('table th:contains("Item Name")').closest('table');
 
     var $tables = $('table th:contains("Item Name")').closest('table');
     $tables.each(function(i, table) {
+
     $tables.each(function (i, table) {
 
       var $table = $(table);
 
       var $table = $(table);
 
       // Make sure it has both "Item Name" and "Stats" columns
 
       // Make sure it has both "Item Name" and "Stats" columns
 
       if (!$table.has('th:contains("Stats")')) return;
 
       if (!$table.has('th:contains("Stats")')) return;
 
+
 
 
       addFilterLink($table, true);
 
       addFilterLink($table, true);
 
     });
 
     });
 
   };
 
   };
 
   addItemFilteringLinks();
 
   addItemFilteringLinks();
 
+
 
 
   //*****************************************
 
   //*****************************************
 
   // End class-based filtering to item tables
 
   // End class-based filtering to item tables
Line 482: Line 525:
 
   if (window.location.pathname.includes('Special:ClassSlotEquip')) {
 
   if (window.location.pathname.includes('Special:ClassSlotEquip')) {
 
     // Add checkboxes UI to the page
 
     // Add checkboxes UI to the page
     $('table')
+
     $('table').before(
      .before('<label><input id="showNoDrop" type="checkbox" checked/> Show No Drop</label> ' +
+
      '<label><input id="showNoDrop" type="checkbox" checked/> Show No Drop</label> ' +
              '<label><input id="showDroppable" type="checkbox" checked/> Show Droppable</label>' /*+
+
        '<label><input id="showDroppable" type="checkbox" checked/> Show Droppable</label>' /*+
 
               '| <label><input id="showEffect" type="checkbox" checked/> Show With Effects</label>' +
 
               '| <label><input id="showEffect" type="checkbox" checked/> Show With Effects</label>' +
               '<label><input id="showNoEffect" type="checkbox" checked/> Show Without Effects</label>'*/);
+
               '<label><input id="showNoEffect" type="checkbox" checked/> Show Without Effects</label>'*/
 +
    );
  
 
     // Handle when drop/no drop box is checked
 
     // Handle when drop/no drop box is checked
     $('#showNoDrop, #showDroppable').change(function() {
+
     $('#showNoDrop, #showDroppable').change(function () {
 
       var showNoDrop = $('#showNoDrop').is(':checked');
 
       var showNoDrop = $('#showNoDrop').is(':checked');
 
       var showDroppable = $('#showDroppable').is(':checked');
 
       var showDroppable = $('#showDroppable').is(':checked');
Line 495: Line 539:
 
       //      .... but those rows do have an old bgcolor="#cccccc" attribute
 
       //      .... but those rows do have an old bgcolor="#cccccc" attribute
 
       //      that we can use to identify (and not hide) them
 
       //      that we can use to identify (and not hide) them
       $('tbody tr[bgcolor!=#cccccc]').each(function(i, el) {
+
       $('tbody tr[bgcolor!=#cccccc]').each(function (i, el) {
 
         var text = $(el).find('td:eq(0) .itemdata').text();
 
         var text = $(el).find('td:eq(0) .itemdata').text();
 
         var isNoDrop = text.includes('NO DROP');
 
         var isNoDrop = text.includes('NO DROP');
Line 501: Line 545:
 
       });
 
       });
 
     });
 
     });
/*
+
    /*
 
Problem: the event handler needs to be merged so unchecking one box (eg. effect) doesn't undo the other (eg. no drop)
 
Problem: the event handler needs to be merged so unchecking one box (eg. effect) doesn't undo the other (eg. no drop)
  
Line 519: Line 563:
 
*/
 
*/
 
   }
 
   }
 
  
 
   // Generic Table Filtering (for Template:TableFilterCheckbox)
 
   // Generic Table Filtering (for Template:TableFilterCheckbox)
   $('.table-filter-checkbox-container').each(function(i, el) {
+
   $('.table-filter-checkbox-container').each(function (i, el) {
 
     var $container = $(el);
 
     var $container = $(el);
 
     var matchSelector = $container.data('match');
 
     var matchSelector = $container.data('match');
 
     var text = $container.text();
 
     var text = $container.text();
     $container.html('<label><input class="table-filter-checkbox" type="checkbox" ' +
+
     $container.html(
                      'value="' + matchSelector + '" checked />' + text + '</label>');
+
      '<label><input class="table-filter-checkbox" type="checkbox" ' +
 +
        'value="' +
 +
        matchSelector +
 +
        '" checked />' +
 +
        text +
 +
        '</label>'
 +
    );
 
   });
 
   });
  
   var shouldRowBeShown = function(tr) {
+
   var shouldRowBeShown = function (tr) {
 
     var $tr = $(tr);
 
     var $tr = $(tr);
 
     // Check all the checkboxes to see if any given row should appear
 
     // Check all the checkboxes to see if any given row should appear
Line 536: Line 585:
 
     return $('.table-filter-checkbox')
 
     return $('.table-filter-checkbox')
 
       .toArray()
 
       .toArray()
       .reduce(function(isShown, checkbox) {
+
       .reduce(function (isShown, checkbox) {
 
         var matchSelector = $(checkbox).val();
 
         var matchSelector = $(checkbox).val();
         var matches = $tr.is(matchSelector) ||
+
         var matches = $tr.is(matchSelector) || !!$tr.has(matchSelector).length;
                      !!$tr.has(matchSelector).length;
+
 
         var isChecked = $(checkbox).attr('checked');
 
         var isChecked = $(checkbox).attr('checked');
 
         return isShown && !!(isChecked || (!isChecked && !matches));
 
         return isShown && !!(isChecked || (!isChecked && !matches));
 
       }, true);
 
       }, true);
   }
+
   };
   $('body').on('change', '.table-filter-checkbox', function(e) {
+
   $('body').on('change', '.table-filter-checkbox', function (e) {
 
     $('table').show();
 
     $('table').show();
     $('table:not(.toc) tbody tr:not([bgcolor="#cccccc"])').each(function(i, tr) {
+
     $('table:not(.toc) tbody tr:not([bgcolor="#cccccc"])').each(function (
 +
      i,
 +
      tr
 +
    ) {
 
       $(tr).toggle(shouldRowBeShown(tr));
 
       $(tr).toggle(shouldRowBeShown(tr));
 
     });
 
     });
 
     // If all of a table's non-header rows are hidden, hide it
 
     // If all of a table's non-header rows are hidden, hide it
     $('table').filter(function(i, table) {
+
     $('table')
      return !$(table).has('tr:visible:not([bgcolor="#cccccc"])').length;
+
      .filter(function (i, table) {
    }).hide();
+
        return !$(table).has('tr:visible:not([bgcolor="#cccccc"])').length;
 +
      })
 +
      .hide();
 
   });
 
   });
  
 
   // Convert youtube template divs into actual iframes
 
   // Convert youtube template divs into actual iframes
   $('.youtube-placeholder').each(function(i, placeholder) {
+
   $('.youtube-placeholder').each(function (i, placeholder) {
 
     var $placeholder = $(placeholder);
 
     var $placeholder = $(placeholder);
 
     var data = $placeholder.data();
 
     var data = $placeholder.data();
Line 563: Line 616:
 
     // @see https://stackoverflow.com/questions/25661182/embed-youtube-video-refused-to-display-in-a-frame-because-it-set-x-frame-opti
 
     // @see https://stackoverflow.com/questions/25661182/embed-youtube-video-refused-to-display-in-a-frame-because-it-set-x-frame-opti
 
     //if (url.includes('/watch?') url = url.replace('/watch?', '/embed?');
 
     //if (url.includes('/watch?') url = url.replace('/watch?', '/embed?');
   
+
 
 
     // TODO: get height/width from template?
 
     // TODO: get height/width from template?
 
     $placeholder.replaceWith(
 
     $placeholder.replaceWith(
 
       '<iframe ' +
 
       '<iframe ' +
         'width="' + (data.width || '') + '" ' +
+
         'width="' +
         'height="' + (data.height || '') + '" ' +
+
        (data.width || '') +
         'src="' + url + '" ' +
+
        '" ' +
 +
         'height="' +
 +
        (data.height || '') +
 +
        '" ' +
 +
         'src="' +
 +
        url +
 +
        '" ' +
 
         'frameborder="0" ' +
 
         'frameborder="0" ' +
 
         'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" ' +
 
         'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" ' +
 
         'allowfullscreen' +
 
         'allowfullscreen' +
      '></iframe>'
+
        '></iframe>'
 
     );
 
     );
 
   });
 
   });
Line 581: Line 640:
  
 
   var fullTitle = $('title').text();
 
   var fullTitle = $('title').text();
   var title = fullTitle.substr(0, fullTitle.length - ' - Project 1999 Wiki'.length);
+
   var title = fullTitle.substr(
   switch(title) {
+
    0,
 +
    fullTitle.length - ' - Project 1999 Wiki'.length
 +
  );
 +
   switch (title) {
 
     case 'Magelo Blue':
 
     case 'Magelo Blue':
 
     case 'Magelo Green':
 
     case 'Magelo Green':
 
     case 'Magelo Red':
 
     case 'Magelo Red':
       $('.createboxInput').on('focus', function() { this.value = ''});
+
       $('.createboxInput').on('focus', function () {
 +
        this.value = '';
 +
      });
 
       break;
 
       break;
 
     // Give the Per-Level Hunting Guide Page its own JS
 
     // Give the Per-Level Hunting Guide Page its own JS
Line 611: Line 675:
 
     case 'Magelo Import':
 
     case 'Magelo Import':
 
       importScript('MediaWiki:MageloImport.js');
 
       importScript('MediaWiki:MageloImport.js');
       break;  
+
       break;
 
     case 'Mobs By Level':
 
     case 'Mobs By Level':
 
       // Basic DOM manipulation (since the wiki won't let us add <input> tags in the wiki text
 
       // Basic DOM manipulation (since the wiki won't let us add <input> tags in the wiki text
       $('#placeholder')
+
       $('#placeholder').replaceWith(
        .replaceWith(
+
        '<form id="form">' +
          '<form id="form">' +
+
          'Class: <input id="class" style="width: 8em" value="Warrior" /> ' +
            'Class: <input id="class" style="width: 8em" value="Warrior" /> ' +
+
          'Level: <input id="level" style="width: 4em" /> ' +
            'Level: <input id="level" style="width: 4em" /> ' +
+
          '<input type="submit"/>' +
            '<input type="submit"/>' +
+
 
           '</form>'
 
           '</form>'
        );
+
      );
       $('#form').submit(function() {
+
       $('#form').submit(function () {
         var clazz = $('#class').val();  
+
         var clazz = $('#class').val();
         var level = $('#level').val();  
+
         var level = $('#level').val();
         window.location = 'https://wiki.project1999.com/index.php?title=Special:Search&limit=500&offset=0&redirs=0&profile=default&search=%22Level%5C%3A%5C+' +
+
         window.location =
        level + '%22+-%22Shopkeeper%22+-%22Merchant%22+%22Class%5C%3A%5C+'+ clazz + '%22+-"startMageloProfile"';
+
          'https://wiki.project1999.com/index.php?title=Special:Search&limit=500&offset=0&redirs=0&profile=default&search=%22Level%5C%3A%5C+' +
 +
          level +
 +
          '%22+-%22Shopkeeper%22+-%22Merchant%22+%22Class%5C%3A%5C+' +
 +
          clazz +
 +
          '%22+-"startMageloProfile"';
 
         return false;
 
         return false;
 
       });
 
       });
Line 634: Line 701:
 
       // the table of contents is shown
 
       // the table of contents is shown
  
       var setNotesColumns = function(cols) { $('#notesWrapper').css('width', cols ? cols + 'em' : ''); };
+
       var setNotesColumns = function (cols) {
       var toggleNotRelevant =function(isChecked) {
+
        $('#notesWrapper').css('width', cols ? cols + 'em' : '');
         $('#notesWrapper').find(':not(:visible)').show()
+
      };
 +
       var toggleNotRelevant = function (isChecked) {
 +
         $('#notesWrapper').find(':not(:visible)').show();
 
         if (isChecked) return;
 
         if (isChecked) return;
  
         $('#notesWrapper').children().filter(function(i, el) {
+
         $('#notesWrapper')
          var isRelevant = $(el).is('.relevant');
+
          .children()
          $(el).toggle(isRelevant);
+
          .filter(function (i, el) {
          // Show the patch header also
+
            var isRelevant = $(el).is('.relevant');
          if (isRelevant) $(el).prevUntil('h3').last().prev().show();
+
            $(el).toggle(isRelevant);
         })
+
            // Show the patch header also
 +
            if (isRelevant) $(el).prevUntil('h3').last().prev().show();
 +
          });
 +
      };
 +
      var toggleTableOfContents = function (isChecked) {
 +
         $('#toc').toggle(isChecked);
 +
      };
 +
      var togglePatchHeaders = function (isChecked) {
 +
        $('.toclevel-4').toggle(isChecked);
 
       };
 
       };
      var toggleTableOfContents = function(isChecked) { $('#toc').toggle(isChecked); };
 
      var togglePatchHeaders = function(isChecked) { $('.toclevel-4').toggle(isChecked); };
 
  
 
       var notesWidth = getStateByPage('notes-width') || '';
 
       var notesWidth = getStateByPage('notes-width') || '';
       var showTableOfContents = getStateByPage('show-table-of-contents') !== false;
+
       var showTableOfContents =
 +
        getStateByPage('show-table-of-contents') !== false;
 
       var showPatchHeaders = getStateByPage('show-patch-headers') !== false;
 
       var showPatchHeaders = getStateByPage('show-patch-headers') !== false;
 
       var showNotRelevant = getStateByPage('show-not-relevant') !== false;
 
       var showNotRelevant = getStateByPage('show-not-relevant') !== false;
Line 660: Line 736:
  
 
       $('#patchNoteOptions').html(
 
       $('#patchNoteOptions').html(
      '<div><label><input ' + (showTableOfContents ? 'checked ' : '') + 'id="show-table-of-contents" type="checkbox" /> Show Table of Contents?<label><br/>' +
+
        '<div><label><input ' +
      '<label><input ' + (showPatchHeaders ? 'checked ' : '') + 'id="show-patch-headers"  type="checkbox"/> Show patch headers in Table of Contents?<label><br/>'+
+
          (showTableOfContents ? 'checked ' : '') +
      '<label><input ' + (showNotRelevant ? 'checked ' : '') + 'id="show-not-relevant"  type="checkbox"/> Show content not marked as relevant to P99?<label></div>' +
+
          'id="show-table-of-contents" type="checkbox" /> Show Table of Contents?<label><br/>' +
      '<div><label>Limit notes to a width of <input id="notes-width" placeholder="40" style="width: 4em;" value="' + notesWidth +'" /> columns (for readability)</label></div>'
+
          '<label><input ' +
 +
          (showPatchHeaders ? 'checked ' : '') +
 +
          'id="show-patch-headers"  type="checkbox"/> Show patch headers in Table of Contents?<label><br/>' +
 +
          '<label><input ' +
 +
          (showNotRelevant ? 'checked ' : '') +
 +
          'id="show-not-relevant"  type="checkbox"/> Show content not marked as relevant to P99?<label></div>' +
 +
          '<div><label>Limit notes to a width of <input id="notes-width" placeholder="40" style="width: 4em;" value="' +
 +
          notesWidth +
 +
          '" /> columns (for readability)</label></div>'
 
       );
 
       );
       $('#notes-width').change(function(e) {
+
       $('#notes-width').change(function (e) {
 
         var cols = parseFloat($(e.target).val());
 
         var cols = parseFloat($(e.target).val());
 
         setNotesColumns(cols);
 
         setNotesColumns(cols);
 
         setStateByPage('notes-width', cols);
 
         setStateByPage('notes-width', cols);
 
       });
 
       });
       $('#show-table-of-contents').change(function(e) {
+
       $('#show-table-of-contents').change(function (e) {
         var isChecked = $(e.target).is(':checked');
+
         var isChecked = $(e.target).is(':checked');
 
         toggleTableOfContents(isChecked);
 
         toggleTableOfContents(isChecked);
 
         setStateByPage('show-table-of-contents', isChecked);
 
         setStateByPage('show-table-of-contents', isChecked);
 
       });
 
       });
       $('#show-patch-headers').change(function(e) {
+
       $('#show-patch-headers').change(function (e) {
         var isChecked = $(e.target).is(':checked');
+
         var isChecked = $(e.target).is(':checked');
 
         togglePatchHeaders(isChecked);
 
         togglePatchHeaders(isChecked);
 
         setStateByPage('show-patch-headers', isChecked);
 
         setStateByPage('show-patch-headers', isChecked);
 
       });
 
       });
       $('#show-not-relevant').change(function(e) {
+
       $('#show-not-relevant').change(function (e) {
         var isChecked = $(e.target).is(':checked');
+
         var isChecked = $(e.target).is(':checked');
 
         toggleNotRelevant(isChecked);
 
         toggleNotRelevant(isChecked);
 
         setStateByPage('show-not-relevant', isChecked);
 
         setStateByPage('show-not-relevant', isChecked);
Line 690: Line 774:
  
 
   // Warn users who accidentally try to edit a templated section
 
   // Warn users who accidentally try to edit a templated section
   if ((window.location + '').includes('title=Template:Namedmobpage&action=edit')) {
+
   if (
     alert('Warning: You are attempting you edit the template for all named mobs.  You probably didn\'t mean to do that: you probably clicked on an edit link somewhere in the page and wound up here.  To avoid this simply go back and use the edit *tab* at the top of the page instead.');
+
    (window.location + '').includes('title=Template:Namedmobpage&action=edit')
 +
  ) {
 +
     alert(
 +
      "Warning: You are attempting you edit the template for all named mobs.  You probably didn't mean to do that: you probably clicked on an edit link somewhere in the page and wound up here.  To avoid this simply go back and use the edit *tab* at the top of the page instead."
 +
    );
 
   }
 
   }
  
Line 704: Line 792:
 
     var isUnderTenLines = $('#wpTextbox1').val().split('\n').length < 10;
 
     var isUnderTenLines = $('#wpTextbox1').val().split('\n').length < 10;
 
     var url = '/' + pageName + '#' + (section + extra).replace(/ /g, '_');
 
     var url = '/' + pageName + '#' + (section + extra).replace(/ /g, '_');
     var message = 'This page uses "transclusion" to show part of another page, specifically the code:\n\n    {{#lsth:' + pageName +
+
     var message =
                  '|[[' + section + ']]' + extra + '}}\n\nIf you want to edit the transcluded section, click "Ok": you will be taken to that page.' +
+
      'This page uses "transclusion" to show part of another page, specifically the code:\n\n    {{#lsth:' +
                  '  If you want to stay on this page, click "Cancel".';
+
      pageName +
 +
      '|[[' +
 +
      section +
 +
      ']]' +
 +
      extra +
 +
      '}}\n\nIf you want to edit the transcluded section, click "Ok": you will be taken to that page.' +
 +
      '  If you want to stay on this page, click "Cancel".';
 
     if (isEdit && isUnderTenLines && confirm(message)) location = url;
 
     if (isEdit && isUnderTenLines && confirm(message)) location = url;
 
   }
 
   }
Line 713: Line 807:
  
 
   var selectedServer = getGlobalState('selectedServer') || 'Blue';
 
   var selectedServer = getGlobalState('selectedServer') || 'Blue';
   var buildTabHtml = function(serverNames) {
+
   var buildTabHtml = function (serverNames) {
     var tabDivs = serverNames.map(function(name, i) {
+
     var tabDivs = serverNames.map(function (name, i) {
       return '<div id="' + name + 'Tab" class="tab' + (name === selectedServer ? ' selected' : '') + '">'+ name + '</div>';
+
       return (
 +
        '<div id="' +
 +
        name +
 +
        'Tab" class="tab' +
 +
        (name === selectedServer ? ' selected' : '') +
 +
        '">' +
 +
        name +
 +
        '</div>'
 +
      );
 
     });
 
     });
     return '<div class="tabs">' + tabDivs.join('') + '<div class="clear"></div></div>';
+
     return (
 +
      '<div class="tabs">' +
 +
      tabDivs.join('') +
 +
      '<div class="clear"></div></div>'
 +
    );
 
   };
 
   };
  
   var selectTab = function(name) {
+
   var selectTab = function (name) {
 
     // show the server's box (and hide the others)
 
     // show the server's box (and hide the others)
 
     $trackers.hide();
 
     $trackers.hide();
Line 730: Line 836:
 
     // Select the tab for that server (and unselect the others)
 
     // Select the tab for that server (and unselect the others)
 
     $('.auctrackerbox .tab').removeClass('selected');
 
     $('.auctrackerbox .tab').removeClass('selected');
     var $tab = $('.auctrackerbox .tab:contains("' + name + '")');
+
     var $tab = $('.auctrackerbox .tab:contains("' + name + '")');
 
     if (!$tab.length) $tab = $('.auctrackerbox .tab:first');
 
     if (!$tab.length) $tab = $('.auctrackerbox .tab:first');
 
     $tab.addClass('selected');
 
     $tab.addClass('selected');
Line 740: Line 846:
 
   if ($trackers.length) {
 
   if ($trackers.length) {
 
     // Convert IDs of "auc_Blue" into an array of ["Blue", "Green", ...]
 
     // Convert IDs of "auc_Blue" into an array of ["Blue", "Green", ...]
     var servers = $trackers.toArray().map(function(tracker) {
+
     var servers = $trackers.toArray().map(function (tracker) {
       return tracker.id.substr(4);  
+
       return tracker.id.substr(4);
 
     });
 
     });
 
     $trackers.prepend(buildTabHtml(servers));
 
     $trackers.prepend(buildTabHtml(servers));
 
     selectTab(selectedServer);
 
     selectTab(selectedServer);
     $trackers.on('click', '.tab', function(e) {
+
     $trackers.on('click', '.tab', function (e) {
 
       selectTab($(e.target).text());
 
       selectTab($(e.target).text());
 
     });
 
     });
Line 756: Line 862:
 
     var forumIds = { blue: 27, green: 77, red: 59, teal: 78 };
 
     var forumIds = { blue: 27, green: 77, red: 59, teal: 78 };
  
   
+
     $('.auctrackerbox span span:contains("Project 1999 Auction Tracker")').each(
     $('.auctrackerbox span span:contains("Project 1999 Auction Tracker")').each(function(i, el) {
+
      function (i, el) {
      var $el = $(el);
+
        var $el = $(el);
      var serverName = $el.closest('.auctrackerbox').attr('id').substr(4).toLowerCase();
+
        var serverName = $el
      const forumSearchUrl = 'https://www.project1999.com/forums/search.php?do=process&forumchoice[]=' +
+
          .closest('.auctrackerbox')
                            forumIds[serverName] + '&query=%22' + $('#firstHeading').text() + '%22';
+
          .attr('id')
      $el.append(
+
          .substr(4)
        '<a ' +
+
          .toLowerCase();
          'style="font-size: 0.5em; float: right" '+
+
        const forumSearchUrl =
          'target="_new" ' +
+
          'https://www.project1999.com/forums/search.php?do=process&forumchoice[]=' +
          'href="' + forumSearchUrl + '"' +
+
          forumIds[serverName] +
        '>Search Forum</a>');
+
          '&query=%22' +
    });
+
          $('#firstHeading').text() +
 +
          '%22';
 +
        $el.append(
 +
          '<a ' +
 +
            'style="font-size: 0.5em; float: right" ' +
 +
            'target="_new" ' +
 +
            'href="' +
 +
            forumSearchUrl +
 +
            '"' +
 +
            '>Search Forum</a>'
 +
        );
 +
      }
 +
    );
 
   }
 
   }
 
});
 
});

Revision as of 21:44, 20 December 2020

/* Any JavaScript here will be loaded for all users on every page load. */

importScript('MediaWiki:Polyfills.js');

// HTTP prevents people from logging in now in (some?) Chrome browsers; redirect to HTTPS
try {
  var onHttp = location.protocol === 'http:';
  var haveNotTriedAlready = !location.search.includes('redirected_from_http');

  if (onHttp && haveNotTriedAlready) {
    location =
      location.origin.replace('http:', 'https:') +
      location.pathname +
      location.search +
      (location.search[0] === '?' ? '&' : '?') +
      'redirected_from_http=1' +
      location.hash;
  }
} catch (err) {
  // Give up on trying to redirect
  // maybe alert user?
}

importScript('MediaWiki:Simple-lightbox.min.js');
importScript('MediaWiki:Zones.js');

// Get all magelos of current user
// (currently un-used, but intended for future achievement code, so that
//  we can have an "add this achievement to my magelo" drop-down with all
//  their magelos listed)
var getMagelosForCurrentUser = function () {
  var userName = $('#pt-userpage a').text();
  var url =
    'https://wiki.project1999.com/index.php?limit=50&tagfilter=&' +
    'title=Special%3AContributions&contribs=user&target=' +
    userName +
    '&namespace=500&topOnly=1&year=&month=-1';
  fetch(url)
    .then(function (response) {
      return response.text();
    })
    .then(function (html) {
      var mageloLinks = Array.from($(html).find('li a:contains("Magelo")'));
      var names = mageloLinks
        .filter(function (i, a) {
          return !$(a).text().includes('(');
        })
        .map(function (i, el) {
          return $(el).text();
        });
      console.log(names);
    });
};

// **********************************
// Add localStorage helper functions
// **********************************

/**
 * Basic "state getter": checks localStorage for *stateKey*, then return the
 * value for *pageName* (if any) found on that object.
 */
var getPageState = function (pageName, stateKey) {
  // Get the state for all pages for this key
  var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');

  // Extract just the state for this page (if any)
  return allPageStates[pageName];
};

/**
 * Returns the state data stored for the provided stateKey and the current page.
 * For instance, getStateByPage('checkbox-status') would return the checkbox\
 * status state for the current page.
 */
window.getStateByPage = function (stateKey) {
  var pageName = location.pathname.substr(1); // ditch the leading "/"
  return getPageState(pageName, stateKey);
};

/**
 * Return the global state for a provided state key
 * (works just like getStateByPage, but for states that don't care
 * about which page you are on)
 */
window.getGlobalState = function (stateKey) {
  return getPageState('__global', stateKey);
};

/**
 * Basic "state setter" function
 */
var setPageState = function (pageName, stateKey, state) {
  // Get the state for this key (for all pages) out of local storage
  var allPageStates = JSON.parse(localStorage.getItem(stateKey) || '{}');

  // Set the state only for this page
  allPageStates[pageName] = state;

  // Put the state for the stateKey (for all pages) back into local storage
  localStorage.setItem(stateKey, JSON.stringify(allPageStates));
};

/**
 * Sets the state data stored for the provided stateKey and the current page.
 * For instance, setStateByPage('checkbox-status', {a: 'b'}) would set the
 * checkbox status state for the current page to an object with a key of "a" and
 * value of "b".
 */
window.setStateByPage = function (stateKey, state) {
  var pageName = location.pathname.substr(1); // ditch the leading "/"
  setPageState(pageName, stateKey, state);
};

window.setGlobalState = function (stateKey, state) {
  return setPageState('__global', stateKey, state);
};

// *******************************

/* p1999wiki.js
 * written by http://wiki.project1999.com/User:Ravhin
 * last update: 7 January, 2018 by Loramin
 */
$(function () {
  var hideDelay = 0;
  var trigDelay = 250;
  var hideTimer = null;
  var ajax = null;

  var currentPosition = { left: '0px', top: '0px' };

  // One instance that's reused to show info for the current person
  var container = $(
    '<div id="itemHoverContainer">' + '<div id="HoverContent"></div>' + '</div>'
  );

  $('body').append(container);

  /* --- hoverbox for item/mob, currently only used in magelo --- */

  // Determine which "a" elements should trigger the item stats mouseover
  var $mouseoverTargets = $('span.ih a');
  var isItemCategory =
    document.title.startsWith('Category:') &&
    document.title.includes('Equipment - Project 1999 Wiki') &&
    !document.title.includes('Worshiper Equipment');
  if (isItemCategory) {
    // Include the category's item list along with ".ih" links
    $mouseoverTargets = $mouseoverTargets.add('.mw-content-ltr a');
  }

  $mouseoverTargets = $mouseoverTargets.filter(function (i, a) {
    // Don't add hover to links like "next 200"
    return (
      !$(a).attr('href').startsWith('/Special:') &&
      !$(a).attr('href').includes('title=Category:')
    );
  });

  $mouseoverTargets.on('mouseover', function () {
    var $this = $(this);
    var itemname = $this.attr('title');

    if (itemname == '' || itemname == 'undefined') return;

    if (hideTimer) clearTimeout(hideTimer);

    if ($this.parents('div.mw-content-ltr').length) {
      var pos = $this.offset();
      var width = $this.width();

      container.css({
        left: pos.left + 5 + 'px',
        top: pos.top + 5 + 'px',
      });
    }

    $(this).trigger('mousemove');

    $('#itemHoverContent').html('&nbsp;');

    //$('#itemHoverContent').html('<div class="itemtopbg"><div class="itemtitle">Loading...</div></div>'
    //                          + '<div class="itembg" style="min-height:50px;"><div class="itemdata">'
    //                          + '<div class="itemicon" style="float:right;"><img alt="" src="/images/Ajax_loader.gif" border="0"></div>'
    //                          + '<p></p></div></div><div class="itembotbg"></div>');

    if (ajax) {
      ajax.abort();
      ajax = null;
    }
    ajax = $.ajax({
      url:
        window.location.protocol +
        '//wiki.project1999.com/index.php/Special:AjaxHoverHelper/' +
        itemname,
      cacheResponse: true,
      success: function (html) {
        var $html = $(html);
        $('#itemHoverContent')
          .html($html[2])
          .prepend($html[0])
          .prepend($html[1]);
      },
    });

    container.css('display', 'block');
    //container.fadeIn('fast');
  }); //on mouseover

  $('span.ih a').on('mouseout', function () {
    if (hideTimer) clearTimeout(hideTimer);
    hideTimer = setTimeout(function () {
      container.css('display', 'none');
      //container.fadeOut('fast');
    }, hideDelay);
  });

  $('span.ih a').mousemove(function (e) {
    var mousex = e.pageX + 20; //Get X coodrinates
    var mousey = e.pageY + 20; //Get Y coordinates
    var tipWidth = container.width(); //Find width of tooltip
    var tipHeight = container.height(); //Find height of tooltip

    //Distance of element from the right edge of viewport
    var tipVisX = $(window).width() - (mousex + tipWidth);
    //Distance of element from the bottom of viewport
    var tipVisY = $(window).height() - (mousey + tipHeight);

    if (tipVisX < 20) {
      //If tooltip exceeds the X coordinate of viewport

      if (tipWidth > e.pageX - 20) {
        mousex = 0;
      } else {
        mousex = e.pageX - tipWidth - 20;
      }
    }
    if (tipVisY < 20) {
      //If tooltip exceeds the Y coordinate of viewport
      mousey = e.pageY - tipHeight - 20;
    }

    container.css({ top: mousey, left: mousex });
  });

  // Allow mouse over of details without hiding details
  $('#itemHoverContainer').mouseover(function () {
    if (hideTimer) clearTimeout(hideTimer);
  });

  // Hide after mouseout
  $('#itemHoverContainer').mouseout(function () {
    if (hideTimer) clearTimeout(hideTimer);
    hideTimer = setTimeout(function () {
      container.css('display', 'none');
      //container.fadeOut('fast');
    }, hideDelay);
  });

  // magelo non-ajax item hover, but move box with mouse
  $('.magelohb').mousemove(function (e) {
    var childContainer = $(this).children('span.hb');

    var tipWidth = childContainer.width(); //Find width of tooltip
    var tipHeight = childContainer.height(); //Find height of tooltip

    var mousex = e.pageX + 20; //Get X coodrinates
    var mousey = e.pageY + 20; //Get Y coordinates

    //Distance of element from the right edge of viewport
    var tipVisX = $(window).width() - (mousex + tipWidth - 20);
    //Distance of element from the bottom of viewport
    var tipVisY = $(window).height() - (mousey + tipHeight - 20);

    if (tipVisX < 20) {
      //If tooltip exceeds the X coordinate of viewport

      if (tipWidth > e.pageX - 20) {
        mousex = 0;
      } else {
        mousex = e.pageX - tipWidth - 20;
      }
    }
    if (tipVisY < 20) {
      //If tooltip exceeds the Y coordinate of viewport
      mousey = e.pageY - tipHeight - 20;
    }

    childContainer.css({ top: mousey, left: mousex, 'z-index': '999' });
  });

  // change to position:fixed on all hover divs if we have JS active
  // otherwise leave as position:absolute so the stationary hovers are near their items
  $('.magelohb span.hb').each(function (i) {
    $(this).css({ position: 'fixed' });
  });

  // Chrome no longer displays alt text when an image is hovered over.  However the wiki only has
  // alt attributes for images, not titles.  This fixes that by converting alt => title
  $('img[alt]').each(function (i, img) {
    $(img).attr('title', $(img).attr('alt'));
  });

  // Fashion for item pages
  var extractFashionHtml = function (html) {
    return $(html)
      .find('.fashion_show, .primary_secondary_show')
      .map(function (i, el) {
        var $el = $(el);
        var data = $el.data();
        data.race = $el.find('.fashion_race').html();
        if ($el.is('.fashion_show')) {
          data.armor = $el.find('.fashion_armor').html();
        }
        return data;
      })
      .toArray();
  };

  var getFashionShows = function (fashionCategory) {
    var url = '/Category:Fashion: ' + fashionCategory;
    return $.get(url).then(extractFashionHtml);
  };

  var getItemPageShows = function () {
    var fashionCategories = $('#catlinks li a')
      .map(function (i, a) {
        return $(a).text();
      })
      .filter(function (i, text) {
        return text.startsWith('Fashion:');
      })
      .map(function (i, text) {
        return text.substr('Fashion: '.length);
      });
    var fashion = fashionCategories[0];
    if (!fashion) return $.when();

    var tint = fashionCategories[1];
    return getFashionShows(fashion).then(function (shows) {
      var sameTint = [];
      var otherTint = [];
      shows.forEach(function (show) {
        var isHeld = show.primaryFashion || show.secondaryFashion;
        var bothHaveSameTint = show.tint === tint;
        var bothHaveNoTint = !(show.tint || tint);
        if (isHeld || bothHaveSameTint || bothHaveNoTint) sameTint.push(show);
        else otherTint.push(show);
      });
      return {
        matches: sameTint,
        partialMatches: otherTint,
        fashion: fashion,
        tint: tint,
      };
    });
  };
  var addFashionSection = function (shows, isFullMatch) {
    var preface = isFullMatch
      ? '<h2>Fashion/Appearance</h2>'
      : '<div>Fashion images with the wrong "tint" (i.e. coloring) which ' +
        "can only be used to get a general sense of the item's " +
        'appearance:</div>';

    // Don't show the explanatory text if there are no shows to explain
    if (!shows.length && !isFullMatch) preface = '';

    var $ul = $('<ul>').append(
      shows.map(function (show) {
        var img = isFullMatch
          ? '<br/><img style="max-height: 100px; max-width: 100px;" src="/images/' +
            show.file.replace(/ /g, '_') +
            '"/>'
          : '';
        return (
          '<li class="fashion-link" data-file="' +
          show.file +
          '">' +
          '<a href="#">' +
          show.gender +
          ' ' +
          show.race +
          img +
          '</a></li>'
        );
      })
    );

    $('#itemfashion').before($('<div>' + preface + '</div>').append($ul));
  };

  var addFashionSections = function () {
    getItemPageShows().then(function (pageShows) {
      if (!pageShows) return;
      var matches = pageShows.matches || [];
      var partialMatches = pageShows.partialMatches || [];
      if (pageShows.matches.length || pageShows.partialMatches.length)
        addFashionSection(pageShows.matches, true);
      if (pageShows.partialMatches.length)
        addFashionSection(pageShows.partialMatches, false);
    });
  };
  addFashionSections();

  //*****************************************
  // Add class-based filtering to item tables
  //*****************************************w

  // Determine whether or not a provided row should be shown for the provided
  // class abbreviation (eg. "BRD")
  var isRowShown = function ($tr, classAbbrev) {
    // Extract classes (and races)
    var classText = $tr.find('td').eq(3).text().split('Class:')[1];
    if (!classText) return true; // Ignore (don't filter) rows without class text

    // remove "Race: " part (if any) and upper-case text
    classText = classText.split('Race:')[0] || classText;
    classText = classText.toUpperCase();

    // determine matching text
    var isMatch = classText.includes(classAbbrev);
    var isAllMatch = classText.includes('ALL');
    var isExcept = classText.includes('ALL EXCEPT');

    // determine matches
    if (isMatch && !isExcept) return true; // (eg. BRD in "BRD")
    if (!isMatch && isExcept) return true; // (eg. BRD in "ALL EXCEPT WIZ")
    if (!isExcept && isAllMatch) return true; // (eg. BRD in "ALL")
    return false;
  };

  var scrollToTable = function ($table) {
    window.setTimeout(function () {
      $('html, body').animate({ scrollTop: $table.offset().top }, 200);
    }, 100);
  };

  // Filter the provided table to only display rows for the provided class
  // abbreviation
  var filterTable = function ($table, classAbbrev) {
    $table.find('tr').each(function (i, tr) {
      var $tr = $(tr);
      var rowIsShown = isRowShown($tr, classAbbrev);
      $tr.toggle(rowIsShown);
    });
    scrollToTable($table);

    addFilterLink($table, false);
    $('.itemsUnfilterLink').click(function () {
      unfilterTable($table);
    });

    alert('Filtered to only show rows containing ' + classAbbrev + ' gear.');
  };

  // Restore a table to its previous, un-filtered state
  var unfilterTable = function ($table) {
    $table.find('tr').show();
    addFilterLink($table, true);
    scrollToTable($table);
  };

  // When a user clicks on a table class filter link, handle it by asking them
  // which class (and then filtering the table for that class)
  var handleItemFilterLinkClick = function (e) {
    var promptMessage =
      'Please enter the three-letter abbreviation for the ' +
      'class you want to filter by (eg. "brd" for "Bard").';

    var $table = $(e.target).closest('table');
    var classAbbrev = prompt(promptMessage).toUpperCase();
    if (!classAbbrev || classAbbrev.length !== 3 || classAbbrev === 'ALL') {
      alert(
        'A 3-digit abbreviation wasn\'t entered (or "All" was entered); ' +
          'filtering disabled'
      );
      unfilterTable($table);
      return false;
    }
    filterTable($table, classAbbrev);
  };

  // Adds either a filter or unfilter link (as determined by showFilter) to the
  // provided table
  var addFilterLink = function ($table, showFilter) {
    var $statsHeaderCells = $table.find('th:contains("Stats")');
    var filterType = showFilter ? 'Filter' : 'Unfilter';
    $statsHeaderCells.html(
      'Stats <span style="float:right">' +
        '<a style="text-decoration: underline" class="items' +
        filterType +
        'Link" href="#">' +
        filterType +
        (showFilter ? ' by Class' : '') +
        '</a></span>'
    );

    var $link = $statsHeaderCells.find('.items' + filterType + 'Link');
    if (showFilter) $link.click(handleItemFilterLinkClick);
    else
      $link.click(function () {
        unfilterTable($table);
      });
  };

  // Add class filtering links to all item tables
  var addItemFilteringLinks = function () {
    var $tables = $('table th:contains("Item Name")').closest('table');
    $tables.each(function (i, table) {
      var $table = $(table);
      // Make sure it has both "Item Name" and "Stats" columns
      if (!$table.has('th:contains("Stats")')) return;

      addFilterLink($table, true);
    });
  };
  addItemFilteringLinks();

  //*****************************************
  // End class-based filtering to item tables
  //*****************************************

  // Add No-Drop-Based filtering to the class equipment pages
  if (window.location.pathname.includes('Special:ClassSlotEquip')) {
    // Add checkboxes UI to the page
    $('table').before(
      '<label><input id="showNoDrop" type="checkbox" checked/> Show No Drop</label> ' +
        '<label><input id="showDroppable" type="checkbox" checked/> Show Droppable</label>' /*+
              '| <label><input id="showEffect" type="checkbox" checked/> Show With Effects</label>' +
              '<label><input id="showNoEffect" type="checkbox" checked/> Show Without Effects</label>'*/
    );

    // Handle when drop/no drop box is checked
    $('#showNoDrop, #showDroppable').change(function () {
      var showNoDrop = $('#showNoDrop').is(':checked');
      var showDroppable = $('#showDroppable').is(':checked');
      // NOTE: Some tables are weird and don't keep their TRs in THEAD
      //       .... but those rows do have an old bgcolor="#cccccc" attribute
      //       that we can use to identify (and not hide) them
      $('tbody tr[bgcolor!=#cccccc]').each(function (i, el) {
        var text = $(el).find('td:eq(0) .itemdata').text();
        var isNoDrop = text.includes('NO DROP');
        $(el).toggle((showNoDrop && isNoDrop) || (showDroppable && !isNoDrop));
      });
    });
    /*
Problem: the event handler needs to be merged so unchecking one box (eg. effect) doesn't undo the other (eg. no drop)

    // Handle when effect/no effect box is checked
    $('#showNoEfefct, #showEffect').change(function() {
      var showNoEffect = $('#showNoEffect').is(':checked');
      var showEffect = $('#showEffect').is(':checked');
      // NOTE: Some tables are weird and don't keep their TRs in THEAD
      //       .... but those rows do have an old bgcolor="#cccccc" attribute
      //       that we can use to identify (and not hide) them
      $('tbody tr[bgcolor!=#cccccc]').each(function(i, el) {
        var text = $(el).find('td:eq(0) .itemdata').text();
        var hasEffect = text.includes('NO DROP');
        $(el).toggle((showEffect && hasEffect) || (showNoEffect && !hasEffect));
      });
    });
*/
  }

  // Generic Table Filtering (for Template:TableFilterCheckbox)
  $('.table-filter-checkbox-container').each(function (i, el) {
    var $container = $(el);
    var matchSelector = $container.data('match');
    var text = $container.text();
    $container.html(
      '<label><input class="table-filter-checkbox" type="checkbox" ' +
        'value="' +
        matchSelector +
        '" checked />' +
        text +
        '</label>'
    );
  });

  var shouldRowBeShown = function (tr) {
    var $tr = $(tr);
    // Check all the checkboxes to see if any given row should appear
    // TODO: Instead of checking *every* checkbox, check ones in the same .filter-group
    return $('.table-filter-checkbox')
      .toArray()
      .reduce(function (isShown, checkbox) {
        var matchSelector = $(checkbox).val();
        var matches = $tr.is(matchSelector) || !!$tr.has(matchSelector).length;
        var isChecked = $(checkbox).attr('checked');
        return isShown && !!(isChecked || (!isChecked && !matches));
      }, true);
  };
  $('body').on('change', '.table-filter-checkbox', function (e) {
    $('table').show();
    $('table:not(.toc) tbody tr:not([bgcolor="#cccccc"])').each(function (
      i,
      tr
    ) {
      $(tr).toggle(shouldRowBeShown(tr));
    });
    // If all of a table's non-header rows are hidden, hide it
    $('table')
      .filter(function (i, table) {
        return !$(table).has('tr:visible:not([bgcolor="#cccccc"])').length;
      })
      .hide();
  });

  // Convert youtube template divs into actual iframes
  $('.youtube-placeholder').each(function (i, placeholder) {
    var $placeholder = $(placeholder);
    var data = $placeholder.data();
    var url = data.url;
    // Youtube gets mad if you try to embed watch URLs
    // @see https://stackoverflow.com/questions/25661182/embed-youtube-video-refused-to-display-in-a-frame-because-it-set-x-frame-opti
    //if (url.includes('/watch?') url = url.replace('/watch?', '/embed?');

    // TODO: get height/width from template?
    $placeholder.replaceWith(
      '<iframe ' +
        'width="' +
        (data.width || '') +
        '" ' +
        'height="' +
        (data.height || '') +
        '" ' +
        'src="' +
        url +
        '" ' +
        'frameborder="0" ' +
        'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" ' +
        'allowfullscreen' +
        '></iframe>'
    );
  });

  importScript('MediaWiki:CheckboxLists.js');
  importScript('MediaWiki:LocMaps.js');

  var fullTitle = $('title').text();
  var title = fullTitle.substr(
    0,
    fullTitle.length - ' - Project 1999 Wiki'.length
  );
  switch (title) {
    case 'Magelo Blue':
    case 'Magelo Green':
    case 'Magelo Red':
      $('.createboxInput').on('focus', function () {
        this.value = '';
      });
      break;
    // Give the Per-Level Hunting Guide Page its own JS
    case 'Per-Level Hunting Guide':
      importScript('MediaWiki:HuntingGuide.js');
      break;
    // The item category search also needs special JS
    case 'Item Category Search':
      importScript('MediaWiki:ItemCategorySearch.js');
      break;
    // So does the Solo Artist Challenge
    case 'Solo Artist Challenge':
      importScript('MediaWiki:SoloArtistChallenge.js');
      break;
    case 'Treasure Hunting Guide':
      importScript('MediaWiki:TreasureHuntingGuide.js');
      break;
    case 'Buff Lines':
      importScript('MediaWiki:BuffLines.js');
      break;
    case 'FashionQuest Finder':
      importScript('MediaWiki:FashionQuest.js');
      break;
    case 'Magelo Import':
      importScript('MediaWiki:MageloImport.js');
      break;
    case 'Mobs By Level':
      // Basic DOM manipulation (since the wiki won't let us add <input> tags in the wiki text
      $('#placeholder').replaceWith(
        '<form id="form">' +
          'Class: <input id="class" style="width: 8em" value="Warrior" /> ' +
          'Level: <input id="level" style="width: 4em" /> ' +
          '<input type="submit"/>' +
          '</form>'
      );
      $('#form').submit(function () {
        var clazz = $('#class').val();
        var level = $('#level').val();
        window.location =
          'https://wiki.project1999.com/index.php?title=Special:Search&limit=500&offset=0&redirs=0&profile=default&search=%22Level%5C%3A%5C+' +
          level +
          '%22+-%22Shopkeeper%22+-%22Merchant%22+%22Class%5C%3A%5C+' +
          clazz +
          '%22+-"startMageloProfile"';
        return false;
      });
      break;
    case 'Patch Notes':
      // Let users control patch note width and how
      // the table of contents is shown

      var setNotesColumns = function (cols) {
        $('#notesWrapper').css('width', cols ? cols + 'em' : '');
      };
      var toggleNotRelevant = function (isChecked) {
        $('#notesWrapper').find(':not(:visible)').show();
        if (isChecked) return;

        $('#notesWrapper')
          .children()
          .filter(function (i, el) {
            var isRelevant = $(el).is('.relevant');
            $(el).toggle(isRelevant);
            // Show the patch header also
            if (isRelevant) $(el).prevUntil('h3').last().prev().show();
          });
      };
      var toggleTableOfContents = function (isChecked) {
        $('#toc').toggle(isChecked);
      };
      var togglePatchHeaders = function (isChecked) {
        $('.toclevel-4').toggle(isChecked);
      };

      var notesWidth = getStateByPage('notes-width') || '';
      var showTableOfContents =
        getStateByPage('show-table-of-contents') !== false;
      var showPatchHeaders = getStateByPage('show-patch-headers') !== false;
      var showNotRelevant = getStateByPage('show-not-relevant') !== false;

      setNotesColumns(notesWidth);
      toggleTableOfContents(showTableOfContents);
      togglePatchHeaders(showPatchHeaders);
      toggleNotRelevant(showNotRelevant);

      $('#patchNoteOptions').html(
        '<div><label><input ' +
          (showTableOfContents ? 'checked ' : '') +
          'id="show-table-of-contents" type="checkbox" /> Show Table of Contents?<label><br/>' +
          '<label><input ' +
          (showPatchHeaders ? 'checked ' : '') +
          'id="show-patch-headers"  type="checkbox"/> Show patch headers in Table of Contents?<label><br/>' +
          '<label><input ' +
          (showNotRelevant ? 'checked ' : '') +
          'id="show-not-relevant"  type="checkbox"/> Show content not marked as relevant to P99?<label></div>' +
          '<div><label>Limit notes to a width of <input id="notes-width" placeholder="40" style="width: 4em;" value="' +
          notesWidth +
          '" /> columns (for readability)</label></div>'
      );
      $('#notes-width').change(function (e) {
        var cols = parseFloat($(e.target).val());
        setNotesColumns(cols);
        setStateByPage('notes-width', cols);
      });
      $('#show-table-of-contents').change(function (e) {
        var isChecked = $(e.target).is(':checked');
        toggleTableOfContents(isChecked);
        setStateByPage('show-table-of-contents', isChecked);
      });
      $('#show-patch-headers').change(function (e) {
        var isChecked = $(e.target).is(':checked');
        togglePatchHeaders(isChecked);
        setStateByPage('show-patch-headers', isChecked);
      });
      $('#show-not-relevant').change(function (e) {
        var isChecked = $(e.target).is(':checked');
        toggleNotRelevant(isChecked);
        setStateByPage('show-not-relevant', isChecked);
      });

      break;
  }

  // Warn users who accidentally try to edit a templated section
  if (
    (window.location + '').includes('title=Template:Namedmobpage&action=edit')
  ) {
    alert(
      "Warning: You are attempting you edit the template for all named mobs.  You probably didn't mean to do that: you probably clicked on an edit link somewhere in the page and wound up here.  To avoid this simply go back and use the edit *tab* at the top of the page instead."
    );
  }

  // Warn uses who try to edit a mostly-transcluded section (and offer to take them to the transcluded page)
  var text = $('#wpTextbox1').val();
  var match = text && text.match(/\{\{\#lsth\:(.*?)\|\[\[(.*?)\]\](.*)\}\}/);
  if (match) {
    var pageName = match[1];
    var section = match[2];
    var extra = match[3] || '';
    var isEdit = (window.location + '').includes('action=edit');
    var isUnderTenLines = $('#wpTextbox1').val().split('\n').length < 10;
    var url = '/' + pageName + '#' + (section + extra).replace(/ /g, '_');
    var message =
      'This page uses "transclusion" to show part of another page, specifically the code:\n\n    {{#lsth:' +
      pageName +
      '|[[' +
      section +
      ']]' +
      extra +
      '}}\n\nIf you want to edit the transcluded section, click "Ok": you will be taken to that page.' +
      '  If you want to stay on this page, click "Cancel".';
    if (isEdit && isUnderTenLines && confirm(message)) location = url;
  }

  // *** AUCTION TRACKER RELATED ***

  var selectedServer = getGlobalState('selectedServer') || 'Blue';
  var buildTabHtml = function (serverNames) {
    var tabDivs = serverNames.map(function (name, i) {
      return (
        '<div id="' +
        name +
        'Tab" class="tab' +
        (name === selectedServer ? ' selected' : '') +
        '">' +
        name +
        '</div>'
      );
    });
    return (
      '<div class="tabs">' +
      tabDivs.join('') +
      '<div class="clear"></div></div>'
    );
  };

  var selectTab = function (name) {
    // show the server's box (and hide the others)
    $trackers.hide();
    var $tracker = $('#auc_' + name).show();

    // If the selected tab doesn't exist on this item, show the first tab instead
    if (!$tracker.length) $tracker = $('.auctrackerbox:first').show();

    // Select the tab for that server (and unselect the others)
    $('.auctrackerbox .tab').removeClass('selected');
    var $tab = $('.auctrackerbox .tab:contains("' + name + '")');
    if (!$tab.length) $tab = $('.auctrackerbox .tab:first');
    $tab.addClass('selected');
    setGlobalState('selectedServer', name);
  };

  var $trackers = $('.auctrackerbox');
  // if we're on an item page with an auction tracker
  if ($trackers.length) {
    // Convert IDs of "auc_Blue" into an array of ["Blue", "Green", ...]
    var servers = $trackers.toArray().map(function (tracker) {
      return tracker.id.substr(4);
    });
    $trackers.prepend(buildTabHtml(servers));
    selectTab(selectedServer);
    $trackers.on('click', '.tab', function (e) {
      selectTab($(e.target).text());
    });

    // Add links to search the forum for items to their auction tracker

    // Forum IDs come from forum URLs, eg. red's auction forum is:
    //   https://www.project1999.com/forums/forumdisplay.php?f=59
    // so  we have "red: 59 " below
    var forumIds = { blue: 27, green: 77, red: 59, teal: 78 };

    $('.auctrackerbox span span:contains("Project 1999 Auction Tracker")').each(
      function (i, el) {
        var $el = $(el);
        var serverName = $el
          .closest('.auctrackerbox')
          .attr('id')
          .substr(4)
          .toLowerCase();
        const forumSearchUrl =
          'https://www.project1999.com/forums/search.php?do=process&forumchoice[]=' +
          forumIds[serverName] +
          '&query=%22' +
          $('#firstHeading').text() +
          '%22';
        $el.append(
          '<a ' +
            'style="font-size: 0.5em; float: right" ' +
            'target="_new" ' +
            'href="' +
            forumSearchUrl +
            '"' +
            '>Search Forum</a>'
        );
      }
    );
  }
});