Add error handling

This commit is contained in:
dr-carlos 2021-01-18 07:31:20 +10:30
parent bf155d7abd
commit e384bda358
3 changed files with 212 additions and 39 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 Build{
/*
* Construct a build.
* @param level : Level of the player.
* @param equipment : List of equipment names that make up the build.
/**
* @description Construct a build.
* @param {Number} level : Level of the player.
* @param {String[]} equipment : List of equipment names that make up the build.
* 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.
* @param {Object[]} inputerrors : List of instances of error-like classes.
*/
constructor(level,equipment, powders){
constructor(level,equipment, powders, inputerrors=[]){
let errors = inputerrors;
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
this.powders = powders;
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.helmet = expandItem(helmet, this.powders[0]);
}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") {
const chestplate = itemMap.get(equipment[1]);
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
this.chestplate = expandItem(chestplate, this.powders[1]);
}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") {
const leggings = itemMap.get(equipment[2]);
this.powders[2] = this.powders[2].slice(0,leggings.slots);
this.leggings = expandItem(leggings, this.powders[2]);
}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") {
const boots = itemMap.get(equipment[3]);
this.powders[3] = this.powders[3].slice(0,boots.slots);
this.boots = expandItem(boots, this.powders[3]);
}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") {
const ring = itemMap.get(equipment[4]);
this.ring1 = expandItem(ring, []);
}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") {
const ring = itemMap.get(equipment[5]);
this.ring2 = expandItem(ring, []);
}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") {
const bracelet = itemMap.get(equipment[6]);
this.bracelet = expandItem(bracelet, []);
}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") {
const necklace = itemMap.get(equipment[7]);
this.necklace = expandItem(necklace, []);
}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") {
const weapon = itemMap.get(equipment[8]);
this.powders[4] = this.powders[4].slice(0,weapon.slots);
this.weapon = expandItem(weapon, this.powders[4]);
}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;
}else if (level > 106){
} else if (level > 106) {
this.level = 106;
}else{
} else if (level <= 106 && level >= 1) {
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.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ];
this.items = this.equipment.concat([this.weapon]);
@ -140,6 +262,13 @@ class Build{
this.damageMultiplier = 1.0;
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

View file

@ -403,29 +403,31 @@ function calculateBuild(save_skp, skp){
equipment[i] = equip;
}
let powderings = [];
let errors = [];
for (const i in powderInputs) {
// read in two characters at a time.
// TODO: make this more robust.
let input = getValue(powderInputs[i]);
let powdering = [];
let errored = false;
while (input) {
let first = input.slice(0, 2);
let powder = powderIDs.get(first);
console.log(powder);
if (powder === undefined) {
throw new TypeError("Invalid powder " + powder + " in slot " + i);
errored = true;
}
powdering.push(powder);
input = input.slice(2);
}
if (errored === true) {
errors.push(new IncorrectInput(getValue(powderInputs[i]), "t6w6", powderInputs[i]));
}
powderings.push(powdering);
}
//level setting
let level = document.getElementById("level-choice").value;
if(level === ""){
level = 106;
}
document.getElementById("level-choice").value = level;
console.log(equipment);
player_build = new Build(document.getElementById("level-choice").value, equipment, powderings, errors);
for (let i of document.getElementsByClassName("hide-container-block")) {
i.style.display = "block";
@ -434,8 +436,6 @@ function calculateBuild(save_skp, skp){
i.style.display = "grid";
}
console.log(equipment);
player_build = new Build(level, equipment, powderings);
console.log(player_build.toString());
displayEquipOrder(document.getElementById("build-order"),player_build.equip_order);
@ -463,21 +463,50 @@ function calculateBuild(save_skp, skp){
calculateBuildStats();
setTitle();
if (player_build.errored)
throw new ListError(player_build.errors);
}
catch (error) {
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);
if (error instanceof ListError) {
for (let i of error.errors) {
if (i instanceof ItemNotFound) {
i.element.textContent = i.message;
} else if (i instanceof IncorrectInput) {
if (document.getElementById(i.id) !== null) {
document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message;
}
} 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"/>
<datalist id="helmet-items">
</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 class="left">
<label id="helmet-slots" for="helmet-powder">X slots</label>
<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>
</tr>
<tr>
@ -46,10 +48,12 @@
<input class="iteminput" list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" />
<datalist id="chestplate-items">
</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 class="left">
<label id="chestplate-slots" for="chestplate-powder">X slots</label>
<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>
</tr>
<tr>
@ -58,10 +62,12 @@
<input class="iteminput" list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" />
<datalist id="leggings-items">
</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 class="left">
<label id="leggings-slots" for="leggings-powder">X slots</label>
<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>
</tr>
<tr>
@ -70,10 +76,12 @@
<input class="iteminput" list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" />
<datalist id="boots-items">
</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 class="left">
<label id="boots-slots" for="boots-powder">X slots</label>
<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>
</tr>
<tr>
@ -83,11 +91,13 @@
<input class="iteminput" list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value=""/>
<datalist id="weapon-items">
</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 class="left">
<br>
<label id="weapon-slots" for="weapon-powder">X slots</label>
<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>
</tr>
</table>
@ -105,6 +115,7 @@
<input class="iteminput" list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1"/>
<datalist id="ring1-items">
</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>
</tr>
<tr>
@ -113,6 +124,7 @@
<input class="iteminput" list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2" />
<datalist id="ring2-items">
</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>
</tr>
<tr>
@ -121,6 +133,7 @@
<input class="iteminput" list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet" />
<datalist id="bracelet-items">
</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>
</tr>
<tr>
@ -129,6 +142,7 @@
<input class="iteminput" list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace"/>
<datalist id="necklace-items">
</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>
</tr>
<tr>
@ -136,6 +150,7 @@
<br/>
<label for="level-choice">Level:</label>
<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>
</tr>
</table>