MediaWiki:Gadget-wikisync-core.js: Difference between revisions

From RuneRealm Wiki
Jump to navigation Jump to search
Content added Content deleted
 
No edit summary
 
Line 1: Line 1:
"use strict";

/**
/**
* WikiSync:
* WikiSync:
Line 17: Line 19:
QC_ACTIVE: "qc-active",
QC_ACTIVE: "qc-active",
QC_INPUT: "qc-input",
QC_INPUT: "qc-input",
QC_ICON: "rs-qc-icon",
QC_ICON: "rs-qc-icon"
};
};

var ENDPOINTS = {
var ENDPOINTS = {
osrs: "https://sync.runescape.wiki/runelite/player/username/STANDARD",
osrs: "https://sync.runescape.wiki/runelite/player/username/STANDARD",
shatteredrelics: "https://sync.runescape.wiki/runelite/player/username/SHATTERED_RELICS_LEAGUE" //use actual url
shatteredrelics:
"https://sync.runescape.wiki/runelite/player/username/SHATTERED_RELICS_LEAGUE", //use actual url
};
};


var questCorrections = {
var questCorrections = {
// Add corrections for API quest names -> wiki names here.
// Add corrections for API quest names -> wiki names here.
// API quest name is the key and the wiki page name is the value.
// API quest name is the key and the wiki page name is the value.
"Desert Treasure II - The Fallen Empire": "Desert Treasure II",
"Desert Treasure II - The Fallen Empire": "Desert Treasure II",
"Recipe for Disaster - Another Cook's Quest" : "Recipe for Disaster/Another Cook's Quest",
"Recipe for Disaster - Another Cook's Quest": "Recipe for Disaster/Another Cook's Quest",
"Recipe for Disaster - Culinaromancer" : "Recipe for Disaster/Defeating the Culinaromancer",
"Recipe for Disaster - Culinaromancer": "Recipe for Disaster/Defeating the Culinaromancer",
"Recipe for Disaster - Mountain Dwarf" : "Recipe for Disaster/Freeing the Mountain Dwarf",
"Recipe for Disaster - Mountain Dwarf": "Recipe for Disaster/Freeing the Mountain Dwarf",
"Recipe for Disaster - Wartface & Bentnoze" : "Recipe for Disaster/Freeing the Goblin generals",
"Recipe for Disaster - Wartface & Bentnoze": "Recipe for Disaster/Freeing the Goblin generals",
"Recipe for Disaster - Pirate Pete" : "Recipe for Disaster/Freeing Pirate Pete",
"Recipe for Disaster - Pirate Pete": "Recipe for Disaster/Freeing Pirate Pete",
"Recipe for Disaster - Lumbridge Guide" : "Recipe for Disaster/Freeing the Lumbridge Guide",
"Recipe for Disaster - Lumbridge Guide": "Recipe for Disaster/Freeing the Lumbridge Guide",
"Recipe for Disaster - Evil Dave" : "Recipe for Disaster/Freeing Evil Dave",
"Recipe for Disaster - Evil Dave": "Recipe for Disaster/Freeing Evil Dave",
"Recipe for Disaster - King Awowogei" : "Recipe for Disaster/Freeing King Awowogei",
"Recipe for Disaster - King Awowogei": "Recipe for Disaster/Freeing King Awowogei",
"Recipe for Disaster - Sir Amik Varze" : "Recipe for Disaster/Freeing Sir Amik Varze",
"Recipe for Disaster - Sir Amik Varze": "Recipe for Disaster/Freeing Sir Amik Varze",
"Recipe for Disaster - Skrach Uglogwee" : "Recipe for Disaster/Freeing Skrach Uglogwee"
"Recipe for Disaster - Skrach Uglogwee": "Recipe for Disaster/Freeing Skrach Uglogwee"
},
},
icons = {
icons = {
yes: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-complete" src="//oldschool.runescape.wiki/images/Yes_check.svg?00000" width="15px" ></span>',
yes: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-complete" src="//oldschool.runescape.wiki/images/Yes_check.svg?00000" width="15px" ></span>',
no: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-not-started" src="//oldschool.runescape.wiki/images/X_mark.svg?00000" width="13px" ></span>',
no: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-not-started" src="//oldschool.runescape.wiki/images/X_mark.svg?00000" width="13px" ></span>'
};
};

var wikisync = {
var wikisync = {
/**
/**
* Startup method
* Startup method
*/
*/
init: function () {
init: function init() {
wikisync.createFields();
wikisync.createFields();
},
},
setCheckboxText: function setCheckboxText(text) {

$(".rs-wikisync-hide-completed .oo-ui-labelElement-label").each(function () {
setCheckboxText: function (text) {
$(this).text(text);
$(".rs-wikisync-hide-completed .oo-ui-labelElement-label").each(
function () {
});
$(this).text(text);
}
);
},
},
hideCompletedEntries: function hideCompletedEntries() {

hideCompletedEntries: function () {
var selected = wikisync.hideCompletedCheckbox.isSelected();
var selected = wikisync.hideCompletedCheckbox.isSelected();
if (selected) {
if (selected) {
Line 69: Line 62:
}
}
},
},

/**
/**
* Creates the input fields for the user's RSN
* Creates the input fields for the user's RSN
*/
*/
createFields: function () {
createFields: function createFields() {
var name;
var name;
if (rs.hasLocalStorage() === true) {
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
$.removeCookie("RSN", {
path: "/"
}); // remove any existing cookies using jQuery, will return false if it doesn't exist so it's fine
name = localStorage.getItem("rsn");
name = localStorage.getItem("rsn");
} else {
} else {
name = wikisync.getCookie("RSN");
name = wikisync.getCookie("RSN");
}
}

var gamemode = "osrs";
var gamemode = "osrs";

$("." + CLASSES.QC_INPUT).each(function () {
$("." + CLASSES.QC_INPUT).each(function () {
var input1 = new OO.ui.TextInputWidget({
var input1 = new OO.ui.TextInputWidget({
placeholder: "Display name",
placeholder: "Display name",
id: "rs-qc-rsn",
id: "rs-qc-rsn"
});
});

if (name) {
if (name) {
// Set input to cookie/localStorage value.
// Set input to cookie/localStorage value.
input1.setValue(name);
input1.setValue(name);
}
}

var button1 = new OO.ui.ButtonInputWidget({
var button1 = new OO.ui.ButtonInputWidget({
label: new OO.ui.HtmlSnippet(
label: new OO.ui.HtmlSnippet('<img src="' + rs.getFileURLCached("RuneLite icon.png") + '" width=' + '"20px" />' + " Look up"),
'<img src="' +
rs.getFileURLCached("RuneLite icon.png") +
'" width=' +
'"20px" />' +
" Look up"
),
title: "Look up player data using RuneLite",
title: "Look up player data using RuneLite",
flags: ["primary", "progressive"],
flags: ["primary", "progressive"],
classes: ["wikisync-lookup-button"],
classes: ["wikisync-lookup-button"]
});
});

var leagueOnly = $(this).hasClass("league-only");
var leagueOnly = $(this).hasClass("league-only");
if (leagueOnly) {
if (leagueOnly) {
gamemode = "shatteredrelics";
gamemode = "shatteredrelics";
}
}
var button1action = function button1action() {

var button1action = function () {
if (rs.hasLocalStorage() === true) {
if (rs.hasLocalStorage() === true) {
localStorage.setItem("rsn", input1.value); // save in localStorage
localStorage.setItem("rsn", input1.value); // save in localStorage
Line 123: Line 105:
button1.on("click", button1action);
button1.on("click", button1action);
input1.on("enter", button1action);
input1.on("enter", button1action);

var helpModalBtn = new OO.ui.ButtonWidget({
var helpModalBtn = new OO.ui.ButtonWidget({
label: "Learn how to enable WikiSync",
label: "Learn how to enable WikiSync",
href: "/w/RuneScape:WikiSync",
href: "/w/RuneScape:WikiSync"
});
});

var hideCompleted = false;
var hideCompleted = false;
if (rs.hasLocalStorage()) {
if (rs.hasLocalStorage()) {
Line 136: Line 116:
}
}
wikisync.hideCompletedCheckbox = new OO.ui.CheckboxInputWidget({
wikisync.hideCompletedCheckbox = new OO.ui.CheckboxInputWidget({
selected: hideCompleted,
selected: hideCompleted
});
});
wikisync.hideCompletedCheckbox.on("change", function () {
wikisync.hideCompletedCheckbox.on("change", function () {
Line 145: Line 125:
$(".wikisync-completed").toggle(!selected);
$(".wikisync-completed").toggle(!selected);
});
});

var fieldset = new OO.ui.FieldsetLayout({
var fieldset = new OO.ui.FieldsetLayout({
id: "rs-qc-form",
id: "rs-qc-form"
});
});
var fieldSetItems = [new OO.ui.FieldLayout(input1, {

var fieldSetItems = [
label: "Username:",
new OO.ui.FieldLayout(input1, { label: "Username:", align: "inline" }),
align: "inline"
];
})];

fieldSetItems.push(button1);
fieldSetItems.push(button1);
fieldset.addItems([
fieldset.addItems([new OO.ui.HorizontalLayout({
new OO.ui.HorizontalLayout({
items: fieldSetItems
}), new OO.ui.FieldLayout(helpModalBtn, {
items: fieldSetItems,
label: "No data found. To use this, enable the WikiSync plugin in RuneLite.",
}),
new OO.ui.FieldLayout(helpModalBtn, {
align: "inline",
label:
classes: ["rs-wikisync-help"]
}), new OO.ui.LabelWidget({
"No data found. To use this, enable the WikiSync plugin in RuneLite.",
label: "Missing some data from RuneLite for this page. Please log in to the game to re-sync.",
align: "inline",
classes: ["rs-wikisync-help"],
classes: ["rs-wikisync-missingdata"]
}), new OO.ui.FieldLayout(wikisync.hideCompletedCheckbox, {
}),
new OO.ui.LabelWidget({
label: "Hide completed",
label:
align: "inline",
classes: ["rs-wikisync-hide-completed"]
"Missing some data from RuneLite for this page. Please log in to the game to re-sync.",
})]);
classes: ["rs-wikisync-missingdata"],
}),
new OO.ui.FieldLayout(wikisync.hideCompletedCheckbox, {
label: "Hide completed",
align: "inline",
classes: ["rs-wikisync-hide-completed"],
}),
]);

if ($(this).hasClass("lighttable")) {
if ($(this).hasClass("lighttable")) {
// If it's a lighttable, insert the fieldset before the table
// If it's a lighttable, insert the fieldset before the table
Line 192: Line 162:
}
}
});
});

if (name) {
if (name) {
// If there is a saved name, load the data for it.
// If there is a saved name, load the data for it.
Line 198: Line 167:
}
}
},
},

/**
/**
* Updates the status text
* Updates the status text
*/
*/
updateStatus: function (text) {
updateStatus: function updateStatus(text) {
mw.notify(text, { tag: "wikisync" });
mw.notify(text, {
tag: "wikisync"
});
},
},

/**
/**
* Sets a cookie
* Sets a cookie
*/
*/
setCookie: function (name, value, days) {
setCookie: function setCookie(name, value, days) {
var expires = "";
var expires = "";
if (days) {
if (days) {
Line 218: Line 187:
document.cookie = name + "=" + (value || "") + expires + "; path=/";
document.cookie = name + "=" + (value || "") + expires + "; path=/";
},
},

/**
/**
* Returns the value of a cookie, or null if it doesn't exist
* Returns the value of a cookie, or null if it doesn't exist
*/
*/
getCookie: function (name) {
getCookie: function getCookie(name) {
var cookie = new RegExp(
var cookie = new RegExp("^(?:.*;)?\\s*" + name + "\\s*=\\s*([^;]+)(?:.*)?$"),
"^(?:.*;)?\\s*" + name + "\\s*=\\s*([^;]+)(?:.*)?$"
),
match = document.cookie.match(cookie);
match = document.cookie.match(cookie);

if (match !== null) {
if (match !== null) {
return match[1];
return match[1];
Line 234: Line 199:
}
}
},
},

/**
/**
* Load data
* Load data
*/
*/
loadData: function (rsn, gamemode) {
loadData: function loadData(rsn, gamemode) {
if (!rsn) {
if (!rsn) {
wikisync.updateStatus("Invalid RSN");
wikisync.updateStatus("Invalid RSN");
return;
return;
}
}

var endpoint = ENDPOINTS[gamemode || "osrs"] || ENDPOINTS.osrs;
var endpoint = ENDPOINTS[gamemode || "osrs"] || ENDPOINTS.osrs;
// Hide help text if it is showing.
// Hide help text if it is showing.
Line 249: Line 212:
$(".rs-wikisync-missingdata").hide();
$(".rs-wikisync-missingdata").hide();
$(".wikisync-success").remove();
$(".wikisync-success").remove();

$.ajax({
$.ajax({
// Get the quest data
// Get the quest data
Line 255: Line 217:
url: endpoint.replace("username", rsn),
url: endpoint.replace("username", rsn),
dataType: "json",
dataType: "json",
success: function (msg) {
success: function success(msg) {
var userQuests = {};
var userQuests = {};
Object.entries(msg.quests).forEach(function (q) {
Object.entries(msg.quests).forEach(function (q) {
Line 277: Line 239:
$(".wikisync-completed").show();
$(".wikisync-completed").show();
$(".wikisync-completed").removeClass("wikisync-completed");
$(".wikisync-completed").removeClass("wikisync-completed");
$("<img>").attr("src", "//oldschool.runescape.wiki/images/f/fb/Yes_check.svg?00000").addClass("wikisync-success").css("width", "20px").css("height", "20px").css("position", "relative").insertAfter(".wikisync-lookup-button");
$("<img>")
var hasAllData = [wikisync.addQuestIcons(userQuests), wikisync.addQuestTable(userQuests, userSkills, msg.achievement_diaries), wikisync.addDiaryTable(msg.achievement_diaries), wikisync.addSkillTable(userSkills), wikisync.addSkillIcons(userSkills), wikisync.addMusicTracks(msg.music_tracks), wikisync.addCombatAchievementTasks(msg.combat_achievements)].every(function (result) {
.attr(
"src",
"//oldschool.runescape.wiki/images/f/fb/Yes_check.svg?00000"
)
.addClass("wikisync-success")
.css("width", "20px")
.css("height", "20px")
.css("position", "relative")
.insertAfter(".wikisync-lookup-button");

var hasAllData = [
wikisync.addQuestIcons(userQuests),
wikisync.addQuestTable(
userQuests,
userSkills,
msg.achievement_diaries
),
wikisync.addDiaryTable(msg.achievement_diaries),
wikisync.addSkillTable(userSkills),
wikisync.addSkillIcons(userSkills),
wikisync.addMusicTracks(msg.music_tracks),
wikisync.addCombatAchievementTasks(msg.combat_achievements),
].every(function (result) {
return result;
return result;
});
});

if (!hasAllData) {
if (!hasAllData) {
$(".rs-wikisync-missingdata").show();
$(".rs-wikisync-missingdata").show();
}
}
},
},
error: function (req) {
error: function error(req) {
$("." + CLASSES.QC_ICON).remove();
$("." + CLASSES.QC_ICON).remove();
wikisync.setCheckboxText("Hide completed");
wikisync.setCheckboxText("Hide completed");
if (req.responseJSON && req.responseJSON.code && req.responseJSON.code === "NO_USER_DATA") {
if (
req.responseJSON &&
req.responseJSON.code &&
req.responseJSON.code === "NO_USER_DATA"
) {
$(".rs-wikisync-help").show();
$(".rs-wikisync-help").show();
} else {
} else {
wikisync.updateStatus("There was a problem loading data for " + rsn);
wikisync.updateStatus("There was a problem loading data for " + rsn);
}
}
},
}
});
});
},
},

/**
/**
* Clicks the Combat Achievement rows
* Clicks the Combat Achievement rows
*/
*/
addCombatAchievementTasks: function (combatAchievements) {
addCombatAchievementTasks: function addCombatAchievementTasks(combatAchievements) {
var combatAchievementTable = $('table.' + CLASSES.QC_ACTIVE + '.ca-tasks');
var combatAchievementTable = $('table.' + CLASSES.QC_ACTIVE + '.ca-tasks');
if (combatAchievementTable.length === 0) {
if (combatAchievementTable.length === 0) {
Line 333: Line 267:
return true;
return true;
}
}

if (combatAchievementTable === null) {
if (combatAchievementTable === null) {
return false;
return false;
}
}

var seen = {};
var seen = {};
combatAchievements.forEach(function (taskId) {
combatAchievements.forEach(function (taskId) {
Line 343: Line 275:
});
});
combatAchievementTable.each(function () {
combatAchievementTable.each(function () {
$(this)
$(this).find("tr[data-ca-task-id]").each(function () {
.find("tr[data-ca-task-id]")
var task_id = $(this).data("ca-task-id");
.each(function () {
if (!!seen[task_id] !== $(this).hasClass("highlight-on")) {
var task_id = $(this).data("ca-task-id");
$(this).click();
}
if (!!seen[task_id] !== $(this).hasClass("highlight-on")) {
$(this).click();
if (seen[task_id]) {
}
$(this).addClass("wikisync-completed");
if (seen[task_id]) {
}
});
$(this).addClass("wikisync-completed");
}
});
});
});

return true;
return true;
},
},

/**
/**
* Clicks the music track rows
* Clicks the music track rows
*/
*/
addMusicTracks: function (musicTracks) {
addMusicTracks: function addMusicTracks(musicTracks) {
var musicTable = $("table." + CLASSES.QC_ACTIVE + ".music-tracks");
var musicTable = $("table." + CLASSES.QC_ACTIVE + ".music-tracks");
if (musicTable.length === 0) {
if (musicTable.length === 0) {
Line 368: Line 296:
return true;
return true;
}
}

if (musicTracks === null) {
if (musicTracks === null) {
// Missing data
// Missing data
return false;
return false;
}
}

var total = 0;
var total = 0;
var completed = 0;
var completed = 0;
musicTable.each(function () {
musicTable.each(function () {
$(this).find("tr[data-music-track-name]").each(function () {
$(this)
.find("tr[data-music-track-name]")
var music_track_name = $(this).data("music-track-name");
if (!!musicTracks[music_track_name] !== $(this).hasClass("highlight-on")) {
.each(function () {
var music_track_name = $(this).data("music-track-name");
$(this).click();
}
if (!!musicTracks[music_track_name] !== $(this).hasClass("highlight-on")) {
if (musicTracks[music_track_name]) {
$(this).click();
}
$(this).addClass("wikisync-completed");
if (musicTracks[music_track_name]) {
completed++;
}
$(this).addClass("wikisync-completed");
completed++;
total++;
}
});
total++;
});
});
});
wikisync.setCheckboxText(
wikisync.setCheckboxText("Hide unlocked tracks (" + completed + "/" + total + " unlocked)");
"Hide unlocked tracks (" + completed + "/" + total + " unlocked)"
);
wikisync.hideCompletedEntries();
wikisync.hideCompletedEntries();
return true;
return true;
},
},

/**
/**
* Append a checkmark/X icon to `element`.
* Append a checkmark/X icon to `element`.
*/
*/
append_icon: function (element, completed) {
append_icon: function append_icon(element, completed) {
if (completed) {
if (completed) {
$(element).append(icons.yes);
$(element).append(icons.yes);
Line 408: Line 329:
}
}
},
},

/**
/**
* Clicks the rows in a table of question and diary tiers. Also appends icons to rows dedicated to skill training
* Clicks the rows in a table of question and diary tiers. Also appends icons to rows dedicated to skill training
*/
*/
addQuestTable: function (quests, skills, achievementDiaries) {
addQuestTable: function addQuestTable(quests, skills, achievementDiaries) {
function splitOnLastOccurence(str, splitOn) {
function splitOnLastOccurence(str, splitOn) {
var index = str.lastIndexOf(splitOn);
var index = str.lastIndexOf(splitOn);
return {
return { before: str.slice(0, index), after: str.slice(index + 1) };
before: str.slice(0, index),
after: str.slice(index + 1)
};
}
}


Line 422: Line 345:
var rowID = $(this).data("rowid");
var rowID = $(this).data("rowid");
var isAchievementDiary = rowID.includes("Diary#");
var isAchievementDiary = rowID.includes("Diary#");

if (isAchievementDiary) {
if (isAchievementDiary) {
// Achievement diary rowIDs are formatted as "$NAME Diary#$TIER", where "$NAME" may contain spaces.
// Achievement diary rowIDs are formatted as "$NAME Diary#$TIER", where "$NAME" may contain spaces.
Line 428: Line 350:
var diaryName = splitOnLastOccurence(rowID, " ").before;
var diaryName = splitOnLastOccurence(rowID, " ").before;
var diaryTier = splitOnLastOccurence(rowID, "#").after;
var diaryTier = splitOnLastOccurence(rowID, "#").after;
if (diaryName in achievementDiaries && diaryTier in achievementDiaries[diaryName] && "complete" in achievementDiaries[diaryName][diaryTier]) {
if (
var diaryCompleted = achievementDiaries[diaryName][diaryTier].complete;
diaryName in achievementDiaries &&
diaryTier in achievementDiaries[diaryName] &&
"complete" in achievementDiaries[diaryName][diaryTier]
) {
var diaryCompleted =
achievementDiaries[diaryName][diaryTier].complete;
if (diaryCompleted !== null) {
if (diaryCompleted !== null) {
if (diaryCompleted !== $(this).hasClass("highlight-on")) {
if (diaryCompleted !== $(this).hasClass("highlight-on")) {
Line 453: Line 370:


// Skill training complete
// Skill training complete
$("table." + CLASSES.QC_ACTIVE + ".oqg-table tr[data-skill][data-skill-level]").each(function () {
$(
"table." + CLASSES.QC_ACTIVE + ".oqg-table tr[data-skill][data-skill-level]"
).each(function () {
var skillName = $(this).data("skill");
var skillName = $(this).data("skill");
skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1);
skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1);
Line 464: Line 379:
return true;
return true;
},
},

// Clicks cells/rows in a table based on skill levels.
// Clicks cells/rows in a table based on skill levels.
addSkillTable: function (skills) {
addSkillTable: function addSkillTable(skills) {
$("table." + CLASSES.QC_ACTIVE + ".skill-table [data-skill][data-skill-level]").each(function () {
$(
"table." + CLASSES.QC_ACTIVE + ".skill-table [data-skill][data-skill-level]"
).each(function () {
var skillName = $(this).data("skill");
var skillName = $(this).data("skill");
var skillLevel = $(this).data("skill-level");
var skillLevel = $(this).data("skill-level");
Line 479: Line 391:
return true;
return true;
},
},

// Clicks rows in a table based on achievement diary task completion
// Clicks rows in a table based on achievement diary task completion
addDiaryTable: function (achievementDiaries) {
addDiaryTable: function addDiaryTable(achievementDiaries) {
var hasAllData = true;
var hasAllData = true;
$("table." + CLASSES.QC_ACTIVE + ".diary-table[data-diary-name][data-diary-tier]").each(function () {
$(
"table." + CLASSES.QC_ACTIVE + ".diary-table[data-diary-name][data-diary-tier]"
).each(function () {
var task_index = -1;
var task_index = -1;
var diaryName = $(this).data("diary-name");
var diaryName = $(this).data("diary-name");
var diaryTier = $(this).data("diary-tier");
var diaryTier = $(this).data("diary-tier");
$(this)
$(this).find("tr").each(function () {
.find("tr")
if (task_index < 0) {
.each(function () {
task_index += 1;
if (task_index < 0) {
return;
task_index += 1;
}
if (diaryName in achievementDiaries && diaryTier in achievementDiaries[diaryName] && achievementDiaries[diaryName][diaryTier].tasks.length > task_index) {
return;
var task_completed = achievementDiaries[diaryName][diaryTier].tasks[task_index];
}
if (
if (task_completed !== null) {
if (task_completed !== $(this).hasClass("highlight-on")) {
diaryName in achievementDiaries &&
diaryTier in achievementDiaries[diaryName] &&
$(this).click();
achievementDiaries[diaryName][diaryTier].tasks.length > task_index
) {
var task_completed =
achievementDiaries[diaryName][diaryTier].tasks[task_index];

if (task_completed !== null) {
if (task_completed !== $(this).hasClass("highlight-on")) {
$(this).click();
}
} else {
hasAllData = false;
}
}
} else {
hasAllData = false;
}
}
task_index += 1;
}
});
task_index += 1;
});
});
});
return hasAllData;
return hasAllData;
},
},

/**
/**
* Adds the icons next to respective quests
* Adds the icons next to respective quests
*/
*/
addQuestIcons: function (quests) {
addQuestIcons: function addQuestIcons(quests) {
$("." + CLASSES.QC_ACTIVE + " a").each(function () {
$("." + CLASSES.QC_ACTIVE + " a").each(function () {
if ($(this).html().toLowerCase() != "expand" || $(this).html().toLowerCase() != "collapse") {
if (
$(this).html().toLowerCase() != "expand" ||
$(this).html().toLowerCase() != "collapse"
) {
var questTitle = $(this).text().trim();
var questTitle = $(this).text().trim();
if (questTitle in quests) {
if (questTitle in quests) {
Line 535: Line 432:
return true;
return true;
},
},

/**
/**
* Adds the icons next to respective skills
* Adds the icons next to respective skills
*/
*/
addSkillIcons: function (userLevels) {
addSkillIcons: function addSkillIcons(userLevels) {
$("." + CLASSES.QC_ACTIVE + " .scp").each(function () {
$("." + CLASSES.QC_ACTIVE + " .scp").each(function () {
var level = $(this).data("level");
var level = $(this).data("level");
Line 549: Line 445:
});
});
return true;
return true;
},
}
};
};

$(wikisync.init);
$(wikisync.init);

Latest revision as of 12:06, 20 October 2024

"use strict";

/**
 * WikiSync:
 * - Utilises our WikiSync API, with data provided by users via RuneLite.
 * - Adds the ability to check a user's completed quests.
 * 
 * Slightly adapted from https://runescape.wiki/w/MediaWiki:Gadget-questchecker-core.js
 *
 * @author Jayden
 * @author Andmcadams
 * @author Jakesterwars
 * @author Cook Me Plox
 * @author Lezed1
 * @author Haidro
 */

var CLASSES = {
  QC_ACTIVE: "qc-active",
  QC_INPUT: "qc-input",
  QC_ICON: "rs-qc-icon"
};
var ENDPOINTS = {
  osrs: "https://sync.runescape.wiki/runelite/player/username/STANDARD",
  shatteredrelics: "https://sync.runescape.wiki/runelite/player/username/SHATTERED_RELICS_LEAGUE" //use actual url
};
var questCorrections = {
    // Add corrections for API quest names -> wiki names here.
    // API quest name is the key and the wiki page name is the value.
    "Desert Treasure II - The Fallen Empire": "Desert Treasure II",
    "Recipe for Disaster - Another Cook's Quest": "Recipe for Disaster/Another Cook's Quest",
    "Recipe for Disaster - Culinaromancer": "Recipe for Disaster/Defeating the Culinaromancer",
    "Recipe for Disaster - Mountain Dwarf": "Recipe for Disaster/Freeing the Mountain Dwarf",
    "Recipe for Disaster - Wartface & Bentnoze": "Recipe for Disaster/Freeing the Goblin generals",
    "Recipe for Disaster - Pirate Pete": "Recipe for Disaster/Freeing Pirate Pete",
    "Recipe for Disaster - Lumbridge Guide": "Recipe for Disaster/Freeing the Lumbridge Guide",
    "Recipe for Disaster - Evil Dave": "Recipe for Disaster/Freeing Evil Dave",
    "Recipe for Disaster - King Awowogei": "Recipe for Disaster/Freeing King Awowogei",
    "Recipe for Disaster - Sir Amik Varze": "Recipe for Disaster/Freeing Sir Amik Varze",
    "Recipe for Disaster - Skrach Uglogwee": "Recipe for Disaster/Freeing Skrach Uglogwee"
  },
  icons = {
    yes: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-complete" src="//oldschool.runescape.wiki/images/Yes_check.svg?00000" width="15px" ></span>',
    no: ' <span class="' + CLASSES.QC_ICON + '"><img class="qc-not-started" src="//oldschool.runescape.wiki/images/X_mark.svg?00000" width="13px" ></span>'
  };
var wikisync = {
  /**
   * Startup method
   */
  init: function init() {
    wikisync.createFields();
  },
  setCheckboxText: function setCheckboxText(text) {
    $(".rs-wikisync-hide-completed .oo-ui-labelElement-label").each(function () {
      $(this).text(text);
    });
  },
  hideCompletedEntries: function hideCompletedEntries() {
    var selected = wikisync.hideCompletedCheckbox.isSelected();
    if (selected) {
      $(".wikisync-completed").hide();
    }
  },
  /**
   * Creates the input fields for the user's RSN
   */
  createFields: function createFields() {
    var name;
    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
      name = localStorage.getItem("rsn");
    } else {
      name = wikisync.getCookie("RSN");
    }
    var gamemode = "osrs";
    $("." + CLASSES.QC_INPUT).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);
      }
      var button1 = new OO.ui.ButtonInputWidget({
        label: new OO.ui.HtmlSnippet('<img src="' + rs.getFileURLCached("RuneLite icon.png") + '" width=' + '"20px" />' + " Look up"),
        title: "Look up player data using RuneLite",
        flags: ["primary", "progressive"],
        classes: ["wikisync-lookup-button"]
      });
      var leagueOnly = $(this).hasClass("league-only");
      if (leagueOnly) {
        gamemode = "shatteredrelics";
      }
      var button1action = function button1action() {
        if (rs.hasLocalStorage() === true) {
          localStorage.setItem("rsn", input1.value); // save in localStorage
        } else {
          wikisync.setCookie("RSN", input1.value, 30); // set a cookie for 30 days
        }
        wikisync.loadData(input1.value, gamemode);
      };
      button1.on("click", button1action);
      input1.on("enter", button1action);
      var helpModalBtn = new OO.ui.ButtonWidget({
        label: "Learn how to enable WikiSync",
        href: "/w/RuneScape:WikiSync"
      });
      var hideCompleted = false;
      if (rs.hasLocalStorage()) {
        if (localStorage.getItem("wikisync-hide-completed") === "true") {
          hideCompleted = true;
        }
      }
      wikisync.hideCompletedCheckbox = new OO.ui.CheckboxInputWidget({
        selected: hideCompleted
      });
      wikisync.hideCompletedCheckbox.on("change", function () {
        var selected = wikisync.hideCompletedCheckbox.isSelected();
        if (rs.hasLocalStorage() === true) {
          localStorage.setItem("wikisync-hide-completed", selected); // save in localStorage
        }
        $(".wikisync-completed").toggle(!selected);
      });
      var fieldset = new OO.ui.FieldsetLayout({
        id: "rs-qc-form"
      });
      var fieldSetItems = [new OO.ui.FieldLayout(input1, {
        label: "Username:",
        align: "inline"
      })];
      fieldSetItems.push(button1);
      fieldset.addItems([new OO.ui.HorizontalLayout({
        items: fieldSetItems
      }), new OO.ui.FieldLayout(helpModalBtn, {
        label: "No data found. To use this, enable the WikiSync plugin in RuneLite.",
        align: "inline",
        classes: ["rs-wikisync-help"]
      }), new OO.ui.LabelWidget({
        label: "Missing some data from RuneLite for this page. Please log in to the game to re-sync.",
        classes: ["rs-wikisync-missingdata"]
      }), new OO.ui.FieldLayout(wikisync.hideCompletedCheckbox, {
        label: "Hide completed",
        align: "inline",
        classes: ["rs-wikisync-hide-completed"]
      })]);
      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);
      }
      // Hide all of the help elements to start with
      $(".rs-wikisync-help").hide();
      $(".rs-wikisync-missingdata").hide();
      if ($(".srl-tasks, .music-tracks").length === 0) {
        // only show checkbox if it's a table with hide-able tasks
        $(".rs-wikisync-hide-completed").hide();
      }
    });
    if (name) {
      // If there is a saved name, load the data for it.
      wikisync.loadData(name, gamemode);
    }
  },
  /**
   * Updates the status text
   */
  updateStatus: function updateStatus(text) {
    mw.notify(text, {
      tag: "wikisync"
    });
  },
  /**
   * Sets a cookie
   */
  setCookie: function setCookie(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 getCookie(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 loadData(rsn, gamemode) {
    if (!rsn) {
      wikisync.updateStatus("Invalid RSN");
      return;
    }
    var endpoint = ENDPOINTS[gamemode || "osrs"] || ENDPOINTS.osrs;
    // Hide help text if it is showing.
    $(".rs-wikisync-help").hide();
    $(".rs-wikisync-missingdata").hide();
    $(".wikisync-success").remove();
    $.ajax({
      // Get the quest data
      type: "GET",
      url: endpoint.replace("username", rsn),
      dataType: "json",
      success: function success(msg) {
        var userQuests = {};
        Object.entries(msg.quests).forEach(function (q) {
          var k = q[0],
            v = q[1];
          // Correct quest names to wiki page names
          if (k in questCorrections) {
            var correctName = questCorrections[k];
            userQuests[correctName] = v;
          } else {
            userQuests[k] = v;
          }
        });
        var userSkills = {};
        Object.entries(msg.levels).forEach(function (q) {
          var k = q[0],
            v = q[1];
          userSkills[k] = v;
        });
        $("." + CLASSES.QC_ICON).remove();
        $(".wikisync-completed").show();
        $(".wikisync-completed").removeClass("wikisync-completed");
        $("<img>").attr("src", "//oldschool.runescape.wiki/images/f/fb/Yes_check.svg?00000").addClass("wikisync-success").css("width", "20px").css("height", "20px").css("position", "relative").insertAfter(".wikisync-lookup-button");
        var hasAllData = [wikisync.addQuestIcons(userQuests), wikisync.addQuestTable(userQuests, userSkills, msg.achievement_diaries), wikisync.addDiaryTable(msg.achievement_diaries), wikisync.addSkillTable(userSkills), wikisync.addSkillIcons(userSkills), wikisync.addMusicTracks(msg.music_tracks), wikisync.addCombatAchievementTasks(msg.combat_achievements)].every(function (result) {
          return result;
        });
        if (!hasAllData) {
          $(".rs-wikisync-missingdata").show();
        }
      },
      error: function error(req) {
        $("." + CLASSES.QC_ICON).remove();
        wikisync.setCheckboxText("Hide completed");
        if (req.responseJSON && req.responseJSON.code && req.responseJSON.code === "NO_USER_DATA") {
          $(".rs-wikisync-help").show();
        } else {
          wikisync.updateStatus("There was a problem loading data for " + rsn);
        }
      }
    });
  },
  /**
   * Clicks the Combat Achievement rows
   */
  addCombatAchievementTasks: function addCombatAchievementTasks(combatAchievements) {
    var combatAchievementTable = $('table.' + CLASSES.QC_ACTIVE + '.ca-tasks');
    if (combatAchievementTable.length === 0) {
      // Page doesn't have Combat Achievement tasks on it
      return true;
    }
    if (combatAchievementTable === null) {
      return false;
    }
    var seen = {};
    combatAchievements.forEach(function (taskId) {
      seen[taskId] = true;
    });
    combatAchievementTable.each(function () {
      $(this).find("tr[data-ca-task-id]").each(function () {
        var task_id = $(this).data("ca-task-id");
        if (!!seen[task_id] !== $(this).hasClass("highlight-on")) {
          $(this).click();
        }
        if (seen[task_id]) {
          $(this).addClass("wikisync-completed");
        }
      });
    });
    return true;
  },
  /**
   * Clicks the music track rows
   */
  addMusicTracks: function addMusicTracks(musicTracks) {
    var musicTable = $("table." + CLASSES.QC_ACTIVE + ".music-tracks");
    if (musicTable.length === 0) {
      // Not a music track page
      return true;
    }
    if (musicTracks === null) {
      // Missing data
      return false;
    }
    var total = 0;
    var completed = 0;
    musicTable.each(function () {
      $(this).find("tr[data-music-track-name]").each(function () {
        var music_track_name = $(this).data("music-track-name");
        if (!!musicTracks[music_track_name] !== $(this).hasClass("highlight-on")) {
          $(this).click();
        }
        if (musicTracks[music_track_name]) {
          $(this).addClass("wikisync-completed");
          completed++;
        }
        total++;
      });
    });
    wikisync.setCheckboxText("Hide unlocked tracks (" + completed + "/" + total + " unlocked)");
    wikisync.hideCompletedEntries();
    return true;
  },
  /**
   * Append a checkmark/X icon to `element`.
   */
  append_icon: function append_icon(element, completed) {
    if (completed) {
      $(element).append(icons.yes);
    } else {
      $(element).append(icons.no);
    }
  },
  /**
   * Clicks the rows in a table of question and diary tiers. Also appends icons to rows dedicated to skill training
   */
  addQuestTable: function addQuestTable(quests, skills, achievementDiaries) {
    function splitOnLastOccurence(str, splitOn) {
      var index = str.lastIndexOf(splitOn);
      return {
        before: str.slice(0, index),
        after: str.slice(index + 1)
      };
    }

    // Quest and diary completion
    $("table." + CLASSES.QC_ACTIVE + ".oqg-table tr[data-rowid]").each(function () {
      var rowID = $(this).data("rowid");
      var isAchievementDiary = rowID.includes("Diary#");
      if (isAchievementDiary) {
        // Achievement diary rowIDs are formatted as "$NAME Diary#$TIER", where "$NAME" may contain spaces.

        var diaryName = splitOnLastOccurence(rowID, " ").before;
        var diaryTier = splitOnLastOccurence(rowID, "#").after;
        if (diaryName in achievementDiaries && diaryTier in achievementDiaries[diaryName] && "complete" in achievementDiaries[diaryName][diaryTier]) {
          var diaryCompleted = achievementDiaries[diaryName][diaryTier].complete;
          if (diaryCompleted !== null) {
            if (diaryCompleted !== $(this).hasClass("highlight-on")) {
              $(this).click();
            }
          }
        }
      } else {
        var questName = rowID;
        if (questName in quests) {
          var questCompleted = quests[questName] == 2;
          if (questCompleted !== $(this).hasClass("highlight-on")) {
            $(this).click();
          }
        }
      }
    });

    // Skill training complete
    $("table." + CLASSES.QC_ACTIVE + ".oqg-table tr[data-skill][data-skill-level]").each(function () {
      var skillName = $(this).data("skill");
      skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1);
      var skillLevel = $(this).data("skill-level");
      var row = $(this).find("th");
      wikisync.append_icon(row, skills[skillName] >= skillLevel);
    });
    return true;
  },
  // Clicks cells/rows in a table based on skill levels.
  addSkillTable: function addSkillTable(skills) {
    $("table." + CLASSES.QC_ACTIVE + ".skill-table [data-skill][data-skill-level]").each(function () {
      var skillName = $(this).data("skill");
      var skillLevel = $(this).data("skill-level");
      var skillCompleted = skills[skillName] >= skillLevel;
      if (skillCompleted !== $(this).hasClass("highlight-on")) {
        $(this).click();
      }
    });
    return true;
  },
  // Clicks rows in a table based on achievement diary task completion
  addDiaryTable: function addDiaryTable(achievementDiaries) {
    var hasAllData = true;
    $("table." + CLASSES.QC_ACTIVE + ".diary-table[data-diary-name][data-diary-tier]").each(function () {
      var task_index = -1;
      var diaryName = $(this).data("diary-name");
      var diaryTier = $(this).data("diary-tier");
      $(this).find("tr").each(function () {
        if (task_index < 0) {
          task_index += 1;
          return;
        }
        if (diaryName in achievementDiaries && diaryTier in achievementDiaries[diaryName] && achievementDiaries[diaryName][diaryTier].tasks.length > task_index) {
          var task_completed = achievementDiaries[diaryName][diaryTier].tasks[task_index];
          if (task_completed !== null) {
            if (task_completed !== $(this).hasClass("highlight-on")) {
              $(this).click();
            }
          } else {
            hasAllData = false;
          }
        }
        task_index += 1;
      });
    });
    return hasAllData;
  },
  /**
   * Adds the icons next to respective quests
   */
  addQuestIcons: function addQuestIcons(quests) {
    $("." + CLASSES.QC_ACTIVE + " a").each(function () {
      if ($(this).html().toLowerCase() != "expand" || $(this).html().toLowerCase() != "collapse") {
        var questTitle = $(this).text().trim();
        if (questTitle in quests) {
          wikisync.append_icon(this, quests[questTitle] === 2);
        }
      }
    });
    return true;
  },
  /**
   * Adds the icons next to respective skills
   */
  addSkillIcons: function addSkillIcons(userLevels) {
    $("." + CLASSES.QC_ACTIVE + " .scp").each(function () {
      var level = $(this).data("level");
      var skill = $(this).data("skill");
      if (typeof level !== "number" || userLevels[skill] === undefined) {
        return;
      }
      wikisync.append_icon(this, userLevels[skill] >= level);
    });
    return true;
  }
};
$(wikisync.init);