Merge pull request #1 from dr-carlos/master

Add Error Handling
This commit is contained in:
hppeng-wynn 2021-01-18 03:09:33 -06:00 committed by GitHub
commit 512bcca83b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 219 additions and 40 deletions

165
build.js
View file

@ -44,19 +44,109 @@ function levelToHPBase(level){
} }
} }
/**
* @description Error to catch items that don't exist.
* @module ItemNotFound
*/
class ItemNotFound {
/**
* @class
* @param {String} item the item name entered
* @param {String} type the type of item
* @param {Boolean} genElement whether to generate an element from inputs
* @param {String} override override for item type
*/
constructor(item, type, genElement, override) {
/**
* @public
* @type {String}
*/
this.message = `Cannot find ${override||type} named ${item}`;
if (genElement)
/**
* @public
* @type {Element}
*/
this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0];
else
this.element = document.createElement("div");
}
}
/**
* @description Error to catch incorrect input.
* @module IncorrectInput
*/
class IncorrectInput {
/**
* @class
* @param {String} input the inputted text
* @param {String} format the correct format
* @param {String} sibling the id of the error node's sibling
*/
constructor(input, format, sibling) {
/**
* @public
* @type {String}
*/
this.message = `${input} is incorrect. Example: ${format}`;
/**
* @public
* @type {String}
*/
this.id = sibling;
}
}
/**
* @description Error that inputs an array of items to generate errors of.
* @module ListError
* @extends Error
*/
class ListError extends Error {
/**
* @class
* @param {Array} errors array of errors
*/
constructor(errors) {
let ret = [];
if (typeof errors[0] == "string") {
super(errors[0]);
} else {
super(errors[0].message);
}
for (let i of errors) {
if (typeof i == "string") {
ret.push(new Error(i));
} else {
ret.push(i);
}
}
/**
* @public
* @type {Object[]}
*/
this.errors = ret;
}
}
/*Class that represents a wynn player's build. /*Class that represents a wynn player's build.
*/ */
class Build{ class Build{
/* /**
* Construct a build. * @description Construct a build.
* @param level : Level of the player. * @param {Number} level : Level of the player.
* @param equipment : List of equipment names that make up the build. * @param {String[]} equipment : List of equipment names that make up the build.
* In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon. * In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon.
* @param powders : Powder application. List of lists of integers (powder IDs). * @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
* In order: Helmet, Chestplate, Leggings, Boots, Weapon. * In order: Helmet, Chestplate, Leggings, Boots, Weapon.
* @param {Object[]} inputerrors : List of instances of error-like classes.
*/ */
constructor(level,equipment, powders, externalStats){ constructor(level,equipment, powders, externalStats, inputerrors=[]){
let errors = inputerrors;
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects. // NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
this.powders = powders; this.powders = powders;
if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") { if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") {
@ -64,67 +154,99 @@ class Build{
this.powders[0] = this.powders[0].slice(0,helmet.slots); this.powders[0] = this.powders[0].slice(0,helmet.slots);
this.helmet = expandItem(helmet, this.powders[0]); this.helmet = expandItem(helmet, this.powders[0]);
}else{ }else{
throw new TypeError("No such helmet named "+ equipment[0]); const helmet = itemMap.get("No Helmet");
this.powders[0] = this.powders[0].slice(0,helmet.slots);
this.helmet = expandItem(helmet, this.powders[0]);
errors.push(new ItemNotFound(equipment[0], "helmet", true));
} }
if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") { if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") {
const chestplate = itemMap.get(equipment[1]); const chestplate = itemMap.get(equipment[1]);
this.powders[1] = this.powders[1].slice(0,chestplate.slots); this.powders[1] = this.powders[1].slice(0,chestplate.slots);
this.chestplate = expandItem(chestplate, this.powders[1]); this.chestplate = expandItem(chestplate, this.powders[1]);
}else{ }else{
throw new TypeError("No such chestplate named "+ equipment[1]); const chestplate = itemMap.get("No Chestplate");
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
this.chestplate = expandItem(chestplate, this.powders[1]);
errors.push(new ItemNotFound(equipment[1], "chestplate", true));
} }
if(itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") { if(itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") {
const leggings = itemMap.get(equipment[2]); const leggings = itemMap.get(equipment[2]);
this.powders[2] = this.powders[2].slice(0,leggings.slots); this.powders[2] = this.powders[2].slice(0,leggings.slots);
this.leggings = expandItem(leggings, this.powders[2]); this.leggings = expandItem(leggings, this.powders[2]);
}else{ }else{
throw new TypeError("No such leggings named "+ equipment[2]); const chestplate = itemMap.get("No Leggings");
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
this.chestplate = expandItem(chestplate, this.powders[1]);
errors.push(new ItemNotFound(equipment[2], "leggings", true));
} }
if(itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") { if(itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") {
const boots = itemMap.get(equipment[3]); const boots = itemMap.get(equipment[3]);
this.powders[3] = this.powders[3].slice(0,boots.slots); this.powders[3] = this.powders[3].slice(0,boots.slots);
this.boots = expandItem(boots, this.powders[3]); this.boots = expandItem(boots, this.powders[3]);
}else{ }else{
throw new TypeError("No such boots named "+ equipment[3]); const boots = itemMap.get("No Boots");
this.powders[3] = this.powders[3].slice(0,boots.slots);
this.boots = expandItem(boots, this.powders[3]);
errors.push(new ItemNotFound(equipment[3], "boots", true));
} }
if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") { if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") {
const ring = itemMap.get(equipment[4]); const ring = itemMap.get(equipment[4]);
this.ring1 = expandItem(ring, []); this.ring1 = expandItem(ring, []);
}else{ }else{
throw new TypeError("No such ring named "+ equipment[4]); const ring = itemMap.get("No Ring 1");
this.ring1 = expandItem(ring, []);
errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring"));
} }
if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") { if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") {
const ring = itemMap.get(equipment[5]); const ring = itemMap.get(equipment[5]);
this.ring2 = expandItem(ring, []); this.ring2 = expandItem(ring, []);
}else{ }else{
throw new TypeError("No such ring named "+ equipment[5]); const ring = itemMap.get("No Ring 2");
this.ring2 = expandItem(ring, []);
errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring"));
} }
if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") { if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") {
const bracelet = itemMap.get(equipment[6]); const bracelet = itemMap.get(equipment[6]);
this.bracelet = expandItem(bracelet, []); this.bracelet = expandItem(bracelet, []);
}else{ }else{
throw new TypeError("No such bracelet named "+ equipment[6]); const bracelet = itemMap.get("No Bracelet");
this.bracelet = expandItem(bracelet, []);
errors.push(new ItemNotFound(equipment[6], "bracelet", true));
} }
if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") { if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") {
const necklace = itemMap.get(equipment[7]); const necklace = itemMap.get(equipment[7]);
this.necklace = expandItem(necklace, []); this.necklace = expandItem(necklace, []);
}else{ }else{
throw new TypeError("No such necklace named "+ equipment[7]); const necklace = itemMap.get("No Necklace");
this.necklace = expandItem(necklace, []);
errors.push(new ItemNotFound(equipment[7], "necklace", true));
} }
if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") { if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") {
const weapon = itemMap.get(equipment[8]); const weapon = itemMap.get(equipment[8]);
this.powders[4] = this.powders[4].slice(0,weapon.slots); this.powders[4] = this.powders[4].slice(0,weapon.slots);
this.weapon = expandItem(weapon, this.powders[4]); this.weapon = expandItem(weapon, this.powders[4]);
}else{ }else{
throw new TypeError("No such weapon named "+ equipment[8]); const weapon = itemMap.get("No Weapon");
this.powders[4] = this.powders[4].slice(0,weapon.slots);
this.weapon = expandItem(weapon, this.powders[4]);
errors.push(new ItemNotFound(equipment[8], "weapon", true));
} }
if(level < 1){ //Should these be constants?
if (level < 1) { //Should these be constants?
this.level = 1; this.level = 1;
}else if (level > 106){ } else if (level > 106) {
this.level = 106; this.level = 106;
}else{ } else if (level <= 106 && level >= 1) {
this.level = level; this.level = level;
} else if (typeof level === "string") {
this.level = level;
errors.push(new IncorrectInput(level, "a number", "level-choice"));
} else {
errors.push("Level is not a string or number.");
} }
document.getElementById("level-choice").value = this.level;
this.level = 106;
this.availableSkillpoints = levelToSkillPoints(this.level); this.availableSkillpoints = levelToSkillPoints(this.level);
this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ]; this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ];
this.items = this.equipment.concat([this.weapon]); this.items = this.equipment.concat([this.weapon]);
@ -144,6 +266,13 @@ class Build{
this.externalStats = externalStats; this.externalStats = externalStats;
this.initBuildStats(); this.initBuildStats();
// Remove every error before adding specific ones
for (let i of document.getElementsByClassName("error")) {
i.textContent = "";
}
this.errors = errors;
if (errors.length > 0) this.errored = true;
} }
/*Returns build in string format /*Returns build in string format

View file

@ -427,29 +427,36 @@ function calculateBuild(save_skp, skp){
equipment[i] = equip; equipment[i] = equip;
} }
let powderings = []; let powderings = [];
let errors = [];
for (const i in powderInputs) { for (const i in powderInputs) {
// read in two characters at a time. // read in two characters at a time.
// TODO: make this more robust. // TODO: make this more robust.
let input = getValue(powderInputs[i]); let input = getValue(powderInputs[i]);
let powdering = []; let powdering = [];
let errorederrors = [];
while (input) { while (input) {
let first = input.slice(0, 2); let first = input.slice(0, 2);
let powder = powderIDs.get(first); let powder = powderIDs.get(first);
console.log(powder); console.log(powder);
if (powder === undefined) { if (powder === undefined) {
throw new TypeError("Invalid powder " + powder + " in slot " + i); errorederrors.push(first);
} else {
powdering.push(powder);
} }
powdering.push(powder);
input = input.slice(2); input = input.slice(2);
} }
if (errorederrors.length > 0) {
if (errorederrors.length > 1)
errors.push(new IncorrectInput(errorederrors.join(""), "t6w6", powderInputs[i]));
else
errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i]));
}
console.log("POWDERING" + powdering);
powderings.push(powdering); powderings.push(powdering);
} }
//level setting
let level = document.getElementById("level-choice").value; console.log(equipment);
if(level === ""){ player_build = new Build(document.getElementById("level-choice").value, equipment, powderings, new Map(), errors);
level = 106;
}
document.getElementById("level-choice").value = level;
for (let i of document.getElementsByClassName("hide-container-block")) { for (let i of document.getElementsByClassName("hide-container-block")) {
i.style.display = "block"; i.style.display = "block";
@ -458,8 +465,7 @@ function calculateBuild(save_skp, skp){
i.style.display = "grid"; i.style.display = "grid";
} }
player_build = new Build(level, equipment, powderings, new Map()); console.log(player_build.toString());
//console.log(player_build.toString());
displayEquipOrder(document.getElementById("build-order"),player_build.equip_order); displayEquipOrder(document.getElementById("build-order"),player_build.equip_order);
@ -486,21 +492,50 @@ function calculateBuild(save_skp, skp){
calculateBuildStats(); calculateBuildStats();
setTitle(); setTitle();
if (player_build.errored)
throw new ListError(player_build.errors);
} }
catch (error) { catch (error) {
let msg = error.stack; if (error instanceof ListError) {
let lines = msg.split("\n"); for (let i of error.errors) {
let header = document.getElementById("header"); if (i instanceof ItemNotFound) {
header.textContent = ""; i.element.textContent = i.message;
for (const line of lines) { } else if (i instanceof IncorrectInput) {
let p = document.createElement("p"); if (document.getElementById(i.id) !== null) {
p.classList.add("itemp"); document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message;
p.textContent = line; }
header.appendChild(p); } else {
let msg = i.stack;
let lines = msg.split("\n");
let header = document.getElementById("header");
header.textContent = "";
for (const line of lines) {
let p = document.createElement("p");
p.classList.add("itemp");
p.textContent = line;
header.appendChild(p);
}
let p2 = document.createElement("p");
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
header.appendChild(p2);
}
}
} else {
let msg = error.stack;
let lines = msg.split("\n");
let header = document.getElementById("header");
header.textContent = "";
for (const line of lines) {
let p = document.createElement("p");
p.classList.add("itemp");
p.textContent = line;
header.appendChild(p);
}
let p2 = document.createElement("p");
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
header.appendChild(p2);
} }
let p2 = document.createElement("p");
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
header.appendChild(p2);
} }
} }

View file

@ -34,10 +34,12 @@
<input class="iteminput" list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet"/> <input class="iteminput" list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet"/>
<datalist id="helmet-items"> <datalist id="helmet-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
<td class="left"> <td class="left">
<label id="helmet-slots" for="helmet-powder">X slots</label> <label id="helmet-slots" for="helmet-powder">X slots</label>
<input class="iteminput" type="text" id="helmet-powder" name="helmet-powder" placeholder="Example: t6t6"/> <input class="iteminput" type="text" id="helmet-powder" name="helmet-powder" placeholder="Example: t6t6"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -46,10 +48,12 @@
<input class="iteminput" list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" /> <input class="iteminput" list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" />
<datalist id="chestplate-items"> <datalist id="chestplate-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
<td class="left"> <td class="left">
<label id="chestplate-slots" for="chestplate-powder">X slots</label> <label id="chestplate-slots" for="chestplate-powder">X slots</label>
<input class="iteminput" type="text" id="chestplate-powder" name="chestplate-powder" /> <input class="iteminput" type="text" id="chestplate-powder" name="chestplate-powder" />
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -58,10 +62,12 @@
<input class="iteminput" list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" /> <input class="iteminput" list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" />
<datalist id="leggings-items"> <datalist id="leggings-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
<td class="left"> <td class="left">
<label id="leggings-slots" for="leggings-powder">X slots</label> <label id="leggings-slots" for="leggings-powder">X slots</label>
<input class="iteminput" type="text" id="leggings-powder" name="leggings-powder" /> <input class="iteminput" type="text" id="leggings-powder" name="leggings-powder" />
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -70,10 +76,12 @@
<input class="iteminput" list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" /> <input class="iteminput" list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" />
<datalist id="boots-items"> <datalist id="boots-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
<td class="left"> <td class="left">
<label id="boots-slots" for="boots-powder">X slots</label> <label id="boots-slots" for="boots-powder">X slots</label>
<input class="iteminput" type="text" id="boots-powder" name="boots-powder" /> <input class="iteminput" type="text" id="boots-powder" name="boots-powder" />
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -83,11 +91,13 @@
<input class="iteminput" list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value=""/> <input class="iteminput" list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value=""/>
<datalist id="weapon-items"> <datalist id="weapon-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
<td class="left"> <td class="left">
<br> <br>
<label id="weapon-slots" for="weapon-powder">X slots</label> <label id="weapon-slots" for="weapon-powder">X slots</label>
<input class="iteminput" type="text" id="weapon-powder" name="weapon-powder" /> <input class="iteminput" type="text" id="weapon-powder" name="weapon-powder" />
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
</table> </table>
@ -105,6 +115,7 @@
<input class="iteminput" list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1"/> <input class="iteminput" list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1"/>
<datalist id="ring1-items"> <datalist id="ring1-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -113,6 +124,7 @@
<input class="iteminput" list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2" /> <input class="iteminput" list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2" />
<datalist id="ring2-items"> <datalist id="ring2-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -121,6 +133,7 @@
<input class="iteminput" list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet" /> <input class="iteminput" list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet" />
<datalist id="bracelet-items"> <datalist id="bracelet-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -129,6 +142,7 @@
<input class="iteminput" list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace"/> <input class="iteminput" list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace"/>
<datalist id="necklace-items"> <datalist id="necklace-items">
</datalist> </datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -136,6 +150,7 @@
<br/> <br/>
<label for="level-choice">Level:</label> <label for="level-choice">Level:</label>
<input class="iteminput" id="level-choice" name="level-choice" placeholder="106" value=""/> <input class="iteminput" id="level-choice" name="level-choice" placeholder="106" value=""/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif;"></p>
</td> </td>
</tr> </tr>
</table> </table>