[ Disclaimer, Create new user --- Wiki markup help, Install P99 ]
MediaWiki:Common.js
From Project 1999 Wiki
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Clear the cache in Tools → Preferences
/* 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 isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
var onHttp = location.protocol === 'http:';
var haveNotTriedAlready = !location.search.includes('redirected_from_http');
if (isChrome && 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(' ');
//$('#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');
// Fix searches with url-encoded text
if (window.location.search.includes('title=Special:Search')) {
var searchText = $('#searchText').val();
var decoded = decodeURIComponent(searchText)
if (decoded !== searchText) {
// If we have URL-encoded text, which won't give results,
// replace it with the decoded text and retry
$('#searchText').val(decoded).closest('form').submit();
}
}
var fullTitle = $('title').text();
var title = fullTitle.substr(
0,
fullTitle.length - ' - Project 1999 Wiki'.length
);
switch (title) {
case 'Admin:Spam Removal':
$('#oneUsePlaceholder').html(`<a href="javascript:const getPageEditToken = async (name) => { const response = await fetch( `https://wiki.project1999.com/index.php?title=${name}&action=delete` ); const html = await response.text(); const index = html.indexOf('wpEditToken'); return html.slice(index - 42, index - 8); }; const getUserEditToken = async (name) => { const response = await fetch( `https://wiki.project1999.com/Special:Block/${encodeURIComponent(name)}` ); const html = await response.text(); const junk = ` id= \"wpEditToken \" type= \"hidden \" value= \"`; const index = html.indexOf(junk); const token = html.slice(index + junk.length).split(' \"')[0]; return token; }; const getCreatorUsername = async (pageName) => { const url = `https://wiki.project1999.com/index.php?title=${pageName}&action=history`; const responseHtml = await fetch(url); const html = await responseHtml.text(); const junkStart = `<span class='history-user'><a href= \"/User:`; const junkEnd = ` \" title= \"`; const startIndex = html.indexOf(junkStart) + junkStart.length; return html.substring(startIndex, html.indexOf(junkEnd, startIndex)); }; const blockUserName = async (username) => { const editToken = await getUserEditToken(username); const encodedName = encodeURIComponent(username); const url = `https://wiki.project1999.com/Special:Block/${encodedName}`; const options = { body: `wpTarget=${username}` + `&wpExpiry=infinite&wpExpiry-other=` + `&wpReason=Spamming+links+to+external+sites` + `&wpReason-other=` + `&wpCreateAccount=1` + `&wpAutoBlock=1` + `&wpEditToken=${encodeURIComponent(editToken)}` + `&title=Special%3ABlock%2F${username}` + `&redirectparams=` + `&wpPreviousTarget=${username}` + `&wpConfirm=`, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', }; const response = await fetch(url, options); const responseHtml = await response.text(); return responseHtml.includes('Block succeeded'); }; const deletePage = async (pageName) => { const creator = await getCreatorUsername(pageName); const banSucceess = await blockUserName(creator); if (!banSucceess) { return alert( `Something went wrong when blocking \"${creator} \"; see Network tab for details.` ); } const url = `https://wiki.project1999.com/index.php?title=${pageName}&action=delete`; const editToken = await getPageEditToken(pageName); const options = { body: `wpDeleteReasonList=Vandalism` + `&wpReason=Content+was+inappropriate` + `&wpConfirmB=Delete+page` + `&wpEditToken=${encodeURIComponent(editToken)}`, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', }; const response = await fetch(url, options); const responseHtml = await response.text(); const deleteSuccess = responseHtml.includes('Action complete'); alert( deleteSuccess ? ` \"${pageName} \" was deleted and its creator \"${creator} \" was banned.` : `Deleting \"${pageName} \" failed: see Network tab for details.` ); }; $('a').one('click', (e) => { deletePage(e.target.innerHTML); return false; }); ">One-Use Spam Deletion`);
$('#multiplePlaceholder').html(`<a href="javascript:const getPageEditToken = async (name) => { const response = await fetch( `https://wiki.project1999.com/index.php?title=${name}&action=delete` ); const html = await response.text(); const index = html.indexOf('wpEditToken'); return html.slice(index - 42, index - 8); }; const getUserEditToken = async (name) => { const response = await fetch( `https://wiki.project1999.com/Special:Block/${encodeURIComponent(name)}` ); const html = await response.text(); const junk = ` id= \"wpEditToken \" type= \"hidden \" value= \"`; const index = html.indexOf(junk); const token = html.slice(index + junk.length).split(' \"')[0]; return token; }; const getCreatorUsername = async (pageName) => { const url = `https://wiki.project1999.com/index.php?title=${pageName}&action=history`; const responseHtml = await fetch(url); const html = await responseHtml.text(); const junkStart = `<span class='history-user'><a href= \"/User:`; const junkEnd = ` \" title= \"`; const startIndex = html.indexOf(junkStart) + junkStart.length; return html.substring(startIndex, html.indexOf(junkEnd, startIndex)); }; const blockUserName = async (username) => { const editToken = await getUserEditToken(username); const encodedName = encodeURIComponent(username); const url = `https://wiki.project1999.com/Special:Block/${encodedName}`; const options = { body: `wpTarget=${username}` + `&wpExpiry=infinite&wpExpiry-other=` + `&wpReason=Spamming+links+to+external+sites` + `&wpReason-other=` + `&wpCreateAccount=1` + `&wpAutoBlock=1` + `&wpEditToken=${encodeURIComponent(editToken)}` + `&title=Special%3ABlock%2F${username}` + `&redirectparams=` + `&wpPreviousTarget=${username}` + `&wpConfirm=`, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', }; const response = await fetch(url, options); const responseHtml = await response.text(); return responseHtml.includes('Block succeeded'); }; const deletePage = async (pageName) => { const creator = await getCreatorUsername(pageName); const banSucceess = await blockUserName(creator); if (!banSucceess) { return alert( `Something went wrong when blocking \"${creator} \"; see Network tab for details.` ); } const url = `https://wiki.project1999.com/index.php?title=${pageName}&action=delete`; const editToken = await getPageEditToken(pageName); const options = { body: `wpDeleteReasonList=Vandalism` + `&wpReason=Content+was+inappropriate` + `&wpConfirmB=Delete+page` + `&wpEditToken=${encodeURIComponent(editToken)}`, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', }; const response = await fetch(url, options); const responseHtml = await response.text(); const deleteSuccess = responseHtml.includes('Action complete'); alert( deleteSuccess ? ` \"${pageName} \" was deleted and its creator \"${creator} \" was banned.` : `Deleting \"${pageName} \" failed: see Network tab for details.` ); }; $('a').click((e) => { deletePage(e.target.innerHTML); return false; }); ">Multi-Use Spam Deletion`);
break;
case 'Damage Calculator':
importScript('MediaWiki:DamageCalculator.js');
break;
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':
case 'Item ID Generator':
importScript('MediaWiki:MageloImport.js');
break;
case 'Qeynos':
var now = new Date();
if (now.getHours() < 9 || now.getHours() > 21) {
var $art = $('[src="/images/thumb/Qeynos_Day_Art.jpg/300px-Qeynos_Day_Art.jpg"]');
$art.attr('src', '/images/thumb/Qeynos_Night_Art.jpg/300px-Qeynos_Night_Art.jpg');
$art.parent().attr('href', 'https://wiki.project1999.com/File:Qeynos_Night_Art.jpg');
}
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>'
);
}
);
}
});