MediaWiki:Gadget-questchecker-core.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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/** <nowiki>
* Adds the ability to check a user's quests and skills against requirements
* on quest pages.
*
* Adapted from https://github.com/MidasLamb/RS-Wiki-Quest-Checker/
*
* This gadget is utilised on the following templates/modules/pages:
* - Module:QuestDetails
* - Template:Infobox achievement
* - Quests/Strategy
* - List of quests
* - List of quests by age
*
* @author JaydenKieran
*/
var ACTIVATOR_CLASS = '.qc-active',
LIGHTTABLE_CLASS = '.lighttable',
LIGHTTABLE_RESET_CLASS = '.ht-reset';
var questCorrections = {
// first value is RuneMetrics name, second is correct (wiki) name
'Recipe for Disaster: Freeing the Goblin Generals': 'Recipe for Disaster: Freeing the Goblin generals',
'Recipe for Disaster: Freeing the Mountain Dwarf': 'Recipe for Disaster: Freeing the Mountain dwarf',
// first value is RuneMetrics name, second is correct (wiki) name without the suffix (miniquest) or (saga)
'A Guild of Our Own (miniquest)':'A Guild of Our Own',
'Bar Crawl (miniquest)':'Bar Crawl',
'Benedict\'s World Tour (miniquest)':'Benedict\'s World Tour',
'Boric\'s Task I (miniquest)':'Boric\'s Task I',
'Boric\'s Task II (miniquest)':'Boric\'s Task II',
'Boric\'s Task III (miniquest)':'Boric\'s Task III',
'Damage Control (miniquest)':'Damage Control',
'Desert Slayer Dungeon (miniquest)':'Desert Slayer Dungeon',
'Doric\'s Task I (miniquest)':'Doric\'s Task I',
'Doric\'s Task II (miniquest)':'Doric\'s Task II',
'Doric\'s Task III (miniquest)':'Doric\'s Task III',
'Doric\'s Task IV (miniquest)':'Doric\'s Task IV',
'Doric\'s Task V (miniquest)':'Doric\'s Task V',
'Doric\'s Task VI (miniquest)':'Doric\'s Task VI',
'Doric\'s Task VII (miniquest)':'Doric\'s Task VII',
'Doric\'s Task VIII (miniquest)':'Doric\'s Task VIII',
'Enter the Abyss (miniquest)': 'Enter the Abyss',
'Eye for an Eye (miniquest)':'Eye for an Eye',
'Final Destination (miniquest)':'Final Destination',
'Flag Fall (miniquest)':'Flag Fall',
'Foreshadowing (miniquest)':'Foreshadowing',
'From Tiny Acorns (miniquest)':'From Tiny Acorns',
'Ghosts from the Past (miniquest)':'Ghosts from the Past',
'Harbinger (miniquest)':'Harbinger',
'Head of the Family (miniquest)':'Head of the Family',
'Hopespear\'s Will (miniquest)':'Hopespear\'s Will',
'In Memory of the Myreque (miniquest)':'In Memory of the Myreque',
'Jed Hunter (miniquest)':'Jed Hunter',
'Koschei\'s Troubles (miniquest)':'Koschei\'s Troubles',
'Lair of Tarn Razorlor (miniquest)':'Lair of Tarn Razorlor',
'Lost Her Marbles (miniquest)':'Lost Her Marbles',
'Mahjarrat Memories (miniquest)':'Mahjarrat Memories',
'One Foot in the Grave (miniquest)':'One Foot in the Grave',
'Purple Cat (miniquest)':'Purple Cat',
'Raksha, the Shadow Colossus (miniquest)':'Raksha, the Shadow Colossus',
'Rebuilding Edgeville (miniquest)':'Rebuilding Edgeville',
'Sheep Shearer (miniquest)':'Sheep Shearer',
'Sins of the Father (miniquest)':'Sins of the Father',
'Spiritual Enlightenment (miniquest)':'Spiritual Enlightenment',
'Tales of Nomad (miniquest)':'Tales of Nomad',
'Tales of the God Wars (miniquest)':'Tales of the God Wars',
'The Curse of Zaros (miniquest)':'The Curse of Zaros',
'The General\'s Shadow (miniquest)':'The General\'s Shadow',
'The Hunt for Surok (miniquest)':'The Hunt for Surok',
'The Lost Toys (miniquest)':'The Lost Toys',
'Tortle Combat (miniquest)':'Tortle Combat',
'Tuai Leit\'s Own (miniquest)':'Tuai Leit\'s Own',
'Wandering Ga\'al (miniquest)':'Wandering Ga\'al',
'Witch\'s Potion (miniquest)':'Witch\'s Potion',
'Nadir (saga)':'Nadir',
'Thok It To \'Em (saga)':'Thok It To \'Em',
'Thok Your Block Off (saga)':'Thok Your Block Off',
'Three\'s Company (saga)':'Three\'s Company',
'The Vault of Shadows (miniquest)':'The Vault of Shadows',
'Vengeance (saga)':'Vengeance',
},
skills = ["Attack","Defence","Strength","Constitution","Ranged","Prayer",
"Magic","Cooking","Woodcutting","Fletching","Fishing","Firemaking",
"Crafting","Smithing","Mining","Herblore","Agility","Thieving","Slayer",
"Farming","Runecrafting","Hunter","Construction","Summoning",
"Dungeoneering","Divination","Invention","Archaeology"], // Used to turn skill ID's into usable names.
conf = mw.config.get([
'wgArticlePath'
]),
icons = {
'yes': '/images/f/fb/Yes_check.svg',
'no': '/images/a/a2/X_mark.svg',
},
self = {
/**
* Startup method
*/
init: function () {
self.createFields();
},
/**
* Resets the lighttables
*/
resetLighttable: function () {
$(ACTIVATOR_CLASS).each(function(index) {
if ($(this).hasClass('lighttable')) {
var reset = $(this).find(LIGHTTABLE_RESET_CLASS)
if (reset.length) {
// This fires the reset click handler specified in Gadget-highlighttable.js
// It's really messy, but there's no other way to do this
reset.trigger('click')
}
}
})
},
/**
* Creates the input fields on the quest page for the user's RSN
*/
createFields: function () {
if (rs.hasLocalStorage() === true) {
$.removeCookie('RSN', { path: '/' }); // remove any existing cookies using jQuery, will return false if it doesn't exist so it's fine
var name = localStorage.getItem('rsn');
} else {
var name = self.getCookie('RSN');
}
$(ACTIVATOR_CLASS).each( function() {
var input1 = new OO.ui.TextInputWidget( { placeholder: 'Display name', id: 'rs-qc-rsn'} );
if (name) { // set input to cookie/localStorage value
input1.setValue(name);
self.loadData(name);
}
var button1 = new OO.ui.ButtonInputWidget( {
label: 'Look up',
flags: [ 'primary', 'progressive' ]
} );
button1.on('click', function() {
if (rs.hasLocalStorage() === true) {
localStorage.setItem('rsn', input1.value); // save in localStorage
} else {
self.setCookie('RSN', input1.value, 30); // set a cookie for 30 days
}
self.loadData(input1.value);
});
var fieldset = new OO.ui.FieldsetLayout( {
id: 'rs-qc-form',
} );
fieldset.addItems( [
new OO.ui.ActionFieldLayout(
input1,
button1
)
] );
if ($(this).hasClass('lighttable')) {
// If it's a lighttable, insert the fieldset before the table
fieldset.$element.insertBefore(this)
} else {
// If not, insert it inside the element that has the class
$(this).prepend( fieldset.$element );
}
} );
},
/**
* Updates the status text
*/
updateStatus: function (text) {
mw.notify( text, { tag: 'questchecker' } );
},
/**
* Sets a cookie
*/
setCookie: function (name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
},
/**
* Returns the value of a cookie, or null if it doesn't exist
*/
getCookie: function (name) {
var cookie = new RegExp("^(?:.*;)?\\s*" + name + "\\s*=\\s*([^;]+)(?:.*)?$"),
match = document.cookie.match(cookie);
if (match !== null) {
return match[1];
} else {
return null;
}
},
/**
* Load data
*/
loadData: function (rsn) {
self.resetLighttable();
if (!rsn) {
self.updateStatus("Invalid RSN");
return;
}
self.updateStatus('Loading data for ' + rsn);
$.ajax({ // Get the quest data
type: "GET",
url: "/cors/m=runemetrics/quests?user=" + rsn,
dataType: "json",
success: function(msg) {
if (msg.quests.length === 0) {
self.updateStatus("Error loading data. The account's RuneMetrics profile may be private.");
console.error("Could not fetch quest data");
} else {
var userQuests = {};
var userQuestPoints = 0;
msg.quests.forEach(function(item, index) {
// Calculate the user's quest points
if (item.status == 'COMPLETED') {
// If quest is completed, add it to the user's total quest points
userQuestPoints = userQuestPoints + item.questPoints;
}
// Correct quest names to wiki page names
if (item.title in questCorrections) {
var correctName = questCorrections[item.title];
userQuests[correctName] = item.status;
} else {
userQuests[item.title] = item.status;
}
});
self.addQuestIcons(userQuests);
self.addQuestPoints(userQuestPoints);
}
}
});
$.ajax({ // Get the skill data.
type: "GET",
url: "/cors/m=runemetrics/profile/profile?user=" + rsn + "&activities=0",
dataType: "json",
success: function(msg) {
if ("error" in msg) {
self.updateStatus("Error loading data. The account's RuneMetrics profile may be private.");
console.error("Could not fetch skills data");
} else {
var userLevels = {};
msg.skillvalues.forEach(function(item, index){
userLevels[skills[item.id]] = item.level;
});
self.addSkillIcons(userLevels);
}
}
});
self.updateStatus('Quest and skills data loaded for ' + rsn);
},
/**
* Adds icons next to respective quest points
*/
addQuestPoints: function (questPoints) {
var qpRegex = /([0-9]+)\squest point/gi;
$(ACTIVATOR_CLASS + " li:not(:has(>ul))").each(function(index) {
var icon = $(this).find('.rs-qc-icon')
var matches = qpRegex.exec($(this).text())
if (matches && matches.length) {
// This is a list element in the format of "[x]" quest points
// Now we get the first capture group, which contains the number
var requiredPoints = matches[1]
if (requiredPoints > questPoints) {
// Does not have the required points
if (icon.length !== 0) {
var existingImg = icon.eq(0).find('img').eq(0);
existingImg.attr('src', icons.no);
existingImg.attr('title', 'You have ' + questPoints + ' quest points.')
} else {
$(this).append(' <span class="rs-qc-icon"><img class="qc-not-started" src="' + icons.no + '" width="13px" title="You have ' + questPoints + ' quest points." ></span> ');
}
} else if (requiredPoints <= questPoints) {
// Has the required points
if (icon.length !== 0) {
var existingImg = icon.eq(0).find('img').eq(0);
existingImg.attr('src', icons.yes);
existingImg.attr('title', 'You have ' + questPoints + ' quest points.')
} else {
$(this).append(' <span class="rs-qc-icon"><img class="qc-complete" src="' + icons.yes + '" width="15px" title="You have ' + questPoints + ' quest points." ></span> ');
}
}
}
});
},
/**
* Adds the icons next to respective quests
*/
addQuestIcons: function (quests) {
$(ACTIVATOR_CLASS + " a").each(function(index) {
if ($(this).html().toLowerCase() != "expand" || $(this).html().toLowerCase() != "collapse") {
var questTitle = $(this).text().trim(),
icon = $(this).find('.rs-qc-icon'),
imgsrc = '';
if ($(this).parents('table' + LIGHTTABLE_CLASS).length) {
/* We are in a highlight table, so we need to handle things differently
Because highlight tables have no exposed API, we have to trigger the click handler
that Gadget-highlighttable.js adds to the rows in order to highlight
and set the data accordingly. It's messy, but it will probably work */
var trs = $(this).parents('tr')
if (trs.length) {
var parent = trs[0]
if (quests[questTitle] == "COMPLETED") {
$(parent).trigger('click')
}
}
} else {
if (quests[questTitle] == "COMPLETED") {
if (icon.length !== 0) {
icon.eq(0).find('img').eq(0).attr('src', icons.yes );
} else {
$(this).append(' <span class="rs-qc-icon"><img class="qc-complete" src="' + icons.yes + '" width="15px" ></span>');
}
}
if (quests[questTitle] == "NOT_STARTED") {
if (icon.length !== 0) {
icon.eq(0).find('img').eq(0).attr('src', icons.no );
} else {
$(this).append(' <span class="rs-qc-icon"><img class="qc-not-started" src="' + icons.no + '" width="13px" ></span>');
}
}
if (quests[questTitle] == "STARTED") {
if (icon.length !== 0) {
icon.eq(0).find('img').eq(0).attr('src', icons.started );
} else {
$(this).append(' <span class="rs-qc-icon"><span class="qc-in-progress"></span></span>');
}
}
}
}
});
},
/**
* Adds the icons next to respective skills
*/
addSkillIcons: function (userLevels) {
$(ACTIVATOR_CLASS + " > ul > li").each(function(index) {
var textArr = $(this).text().split(" "),
level = textArr[0],
imgsrc = "";
if (isNaN(level)) { //Check if it is a number.
return;
}
var skillElement = $(this).find("a").filter(function(index) {
// Find the link element which has a skill name.
return ($(this).text().trim() in userLevels);
});
skillElement.each(function(index) {
var skill = $(this).text().trim(),
icon = $(this).find('.rs-qc-icon');
if (level !== "" && skill !== ""){
if (userLevels[skill] >= level) {
if (icon.length !== 0) {
icon.eq(0).find('img').eq(0).attr('src', icons.yes );
} else {
skillElement.append(' <span class="rs-qc-icon"><img src="' + icons.yes + '" width="15px" ></span>');
}
} else {
if (icon.length !== 0) {
icon.eq(0).find('img').eq(0).attr('src', icons.no );
} else {
skillElement.append(' <span class="rs-qc-icon"><img src="' + icons.no + '" width="15px" ></span>');
}
}
}
})
});
}
};
$(self.init);
// </nowiki>