MediaWiki:Gadget-tgcLinks.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// Change the 'href' attributes of links that are redirects to the Terraria Wiki
// to make them go directly to their destination. Add a CSS class to those links
// which applies a slightly different color to them.
var cssClass = 'tgclink';
$(document).ready(function() {
var allRedirectLinksOnPage = collectAllRedirectLinks();
// modify the title and href attributes of a link and add the class for coloring
handleDirectInterwikiLinks(allRedirectLinksOnPage.direct);
handleIndirectInterwikiLinks(allRedirectLinksOnPage.indirect);
});
function makeNewHovertext(title) {
return 'Terraria Wiki: ' + title + ' (this link is a redirect to the main Terraria Wiki)';
}
function collectAllRedirectLinks() {
// a direct interwiki link is e.g. "[[tgc:Foo]]";
// an indirect interwiki link is e.g. "[[Foo]]" where the page "Foo"
// contains the interwiki redirect: "#REDIRECT [[tgc:Foo]]"
var directInterwikiLinks = [];
var indirectInterwikiLinks = [];
$('#mw-content-text a.mw-redirect, #mw-content-text a.extiw').each(function() {
var thislink = $(this);
if (thislink.attr('title').slice(0, 4) === 'tgc:') {
directInterwikiLinks.push(thislink);
} else {
indirectInterwikiLinks.push(thislink);
}
});
return { direct: directInterwikiLinks, indirect: indirectInterwikiLinks };
}
function handleDirectInterwikiLinks(links) {
// these `links` are direct interwiki links, their href attribute is correct
// already, and they don't need coloring, so simply modify their title attribute
$.each(links, function() {
var thislink = $(this);
var newHovertext = makeNewHovertext(thislink.attr('title').slice(4)); // strip the 'tgc:' part
thislink.attr('title', newHovertext);
});
}
function handleIndirectInterwikiLinks(links) {
// these `links` are indirect interwiki links, so their href and title attributes
// must be modified and they need coloring
// get the link targets for each of the links (uniquely)
var linkTargets = [];
$.each(links, function() {
var linkTarget = getTargetOfLink($(this));
if ($.inArray(linkTarget, linkTargets) === -1) {
linkTargets.push(linkTarget);
}
});
if (linkTargets.length === 0) {
// there are no redirect links to handle
return;
}
// get the tgc wiki URL for each link target
getRedirectUrls(linkTargets).done(function(redirectUrls) {
if (redirectUrls === undefined) {
return;
}
// modify the links
$.each(links, function() {
var thislink = $(this);
var linkTarget = getTargetOfLink(thislink);
var tgcUrl = redirectUrls[linkTarget];
if (tgcUrl === undefined) {
return;
}
// if the link has a fragment, then make sure to keep it
var hashIndex = thislink.attr('href').indexOf('#');
if (hashIndex > -1) {
tgcUrl += thislink.attr('href').slice(hashIndex);
}
thislink
.attr('href', tgcUrl) // modify the URL of this link
.attr('title', makeNewHovertext(linkTarget)) // modify the hovertext of this link
.addClass(['extiw', cssClass]); // apply coloring to this link
});
});
}
function getTargetOfLink(linkElement) {
// get the target page name of the `linkElement` by stripping "/" and "/wiki/"
// at the beginning and removing the first "#" and everything after it
return decodeURIComponent(linkElement.attr('href')
.replace(/_/g, ' ')
.replace(/^\/(wiki\/)?/, '')
.replace(/#.+$/, '')
);
}
function getRedirectUrls(linkTargets) {
// query the API and return an object where each of the `linkTargets` has a tgc wiki URL
var deferred = new $.Deferred();
new mw.Api().post({ // not .get() because the `linkTargets` can be quite long
action: 'query',
format: 'json',
titles: linkTargets.join('|'),
redirects: true,
iwurl: true
}).done(function(data) {
if (data.query.redirects === undefined || data.query.interwiki === undefined) {
deferred.resolve();
return;
}
var redirectUrls = {};
// the `data.query` is two arrays, e.g. for titles="Mount|Accessory" like this:
// (https://terrariamods.wiki.gg/api.php?action=query&redirects=&iwurl=&titles=Mount|Accessory)
/* {
"redirects": [
{ "from": "Accessory", "to": "tgc:Accessories", "tointerwiki": "tgc" },
{ "from": "Mount", "to": "tgc:Mounts", "tointerwiki": "tgc" }
],
"interwiki": [
{ "title": "tgc:Accessories", "iw": "tgc", "url": "https://terraria.wiki.gg/wiki/Accessories" },
{ "title": "tgc:Mounts", "iw": "tgc", "url": "https://terraria.wiki.gg/wiki/Mounts" }
]
} */
// we want to connect the `redirects.from` strings with the `interwiki.url` strings;
// we iterate over each object in `redirects`, but we skip the objects
// where `tointerwiki` is not 'tgc' (e.g. 'clm', or none at all)
var redirectObjects = data.query.redirects.filter(function(r) { return r.tointerwiki === 'tgc'; });
redirectObjects.forEach(function(redirect) {
// connect: find the corresponding object in `interwiki`
var iwObject = data.query.interwiki.find(function (i) {
return i.iw === 'tgc' && i.title === redirect.to;
});
if (iwObject !== undefined) {
// found it
redirectUrls[redirect.from] = iwObject.url;
}
});
// now `redirectUrls` is e.g.:
/* {
"Accessory": "https://terraria.wiki.gg/wiki/Accessories",
"Mount": "https://terraria.wiki.gg/wiki/Mounts"
} */
deferred.resolve(redirectUrls);
}).fail(function() {
// the API threw an error
var error = arguments;
if (error[0] === 'toomanyvalues') {
// there are too many `linkTargets` for one query (e.g. more than
// 50 for a logged-out user), the exact limit in this case is this:
var limit = error[1].error.limit;
// split the `linkTargets` into multiple sub-arrays, each of which
// is no larger than allowed by `limit`
// (e.g. if `linkTargets` has 53 elements and `limit` is 50, then
// split it into `[...(50)]` and `[...(3)]`)
// then repeat the API query for each of those sub-arrays and combine
// the results
var deferreds = [];
for (var index = 0; index < Math.ceil(linkTargets.length / limit); index++) {
// split into sub-array
var linkTargetsSlice = linkTargets.slice(index * limit, (index + 1) * limit);
// do API query
deferreds.push(getRedirectUrls(linkTargetsSlice));
}
// wait for all queries to complete; for an explanation of this method see
// https://stackoverflow.com/questions/5627284/pass-in-an-array-of-deferreds-to-when
// https://stackoverflow.com/questions/4878887/how-do-you-work-with-an-array-of-jquery-deferreds
$.when.apply(null, deferreds).done(function() {
// the arguments to this function are our desired result objects,
// so we need to combine them into one
// iterate over the arguments and merge them into one object
// (named `redirectUrlsCombined` in the callback of reduce() below)
var redirectUrls = Array.from(arguments).reduce(function(redirectUrlsCombined, redirectUrlsSlice) {
// `redirectUrlsSlice` is the result of the API query for one
// of the sub-arrays
if (redirectUrlsSlice !== undefined) {
// merge objects
Object.keys(redirectUrlsSlice).forEach(function(key) {
redirectUrlsCombined[key] = redirectUrlsSlice[key];
});
}
return redirectUrlsCombined;
}, {});
// now `redirectUrls` is merged and we can return it like normal
deferred.resolve(redirectUrls);
});
} else {
console.error(error);
deferred.resolve();
}
});
return deferred;
}