NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name PupilPath Plus // @namespace https://github.com/DeathHackz/PupilPathPlus // @version 4.0.2 // @description Calculate Your PupilPath Cumulative Average & More // @match https://*.pupilpath.skedula.com/* // @author DeathHackz // @copyright 2019 DeathHackz // @license MIT // @homepageURL https://deathhackz.github.io/PupilPathPlus // @supportURL https://github.com/DeathHackz/PupilPathPlus/issues // @icon https://github.com/DeathHackz/PupilPathPlus/raw/master/icon.png // @updateURL https://raw.githubusercontent.com/DeathHackz/PupilPathPlus/master/src/PupilPathPlus.meta.js // @downloadURL https://raw.githubusercontent.com/DeathHackz/PupilPathPlus/master/src/PupilPathPlus.user.js // @run-at document-body // @grant GM_registerMenuCommand /* global jQuery */ // ==/UserScript== /* ================================================================================================================================= PupilPath, and all related icons/images/assets, and names belong to IO Education I am NOT affiliated with PupilPath or IO Education by no means, neither is this script ================================================================================================================================= MIT License Copyright (c) 2019 DeathHackz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================================================================================================= Supported Userscript Extensions: * Tampermonkey (recommended) * Violentmonkey ================================================================================================================================= PupilPath Default Libraries * jQuery v1.5.1 * jQuery-UI v1.8.11 CUSTOM * jQuery FaceBox v1.2 * jQuery Timepicker Addon v0.9.3 ================================================================================================================================= */ // Make Entire Script Run In Strict Mode "use strict"; // Script Version const scriptVersion = "4.0.2"; // Add Menu Items To UserScript Manager GM_registerMenuCommand("Visit Homepage", () => { window.open("https://deathhackz.github.io/PupilPathPlus"); }); GM_registerMenuCommand("Check For Updates", () => { window.open("https://raw.githubusercontent.com/DeathHackz/PupilPathPlus/master/src/PupilPathPlus.user.js"); }); // Console Warning Message // SetTimeout Is Used To Remove The Source Link setTimeout( console.warn.bind( // Styling Is Used Within The Console Message console, `%c \n %c You Are Using PupilPath Plus%cv${scriptVersion} %c \n %c Follow %c@DeathHackz%con GitHub `, "", "background: #383b3e; color: #bada55; padding: 4px; padding-left: 0px; border-top-left-radius: 2px;", "background: #383b3e; color: #C8FF19; padding: 4px; padding-right: 0px; border-top-right-radius: 2px;", "", "background: #383b3e; color: #bada55; padding: 4px; padding-left: 2.8px; border-bottom-left-radius: 2px;", "background: #383b3e; color: #C8FF19; padding-top: 4px; padding-bottom: 4px;", "background: #383b3e; color: #bada55; padding: 4px; padding-right: 0px; padding-left: 8px; border-bottom-right-radius: 2px;", "\nDeathHackz: https://github.com/DeathHackz", "\nHomepage: https://deathhackz.github.io/PupilPathPlus", "\nGitHub: https://github.com/DeathHackz/PupilPathPlus", "\nSupport: https://github.com/DeathHackz/PupilPathPlus/issues\n ", ) ); // Define All Functions Before Conditional Statement // Function Accepts An Array And Returns The Average function calculateAverage(grades) { // Adds Up All The Values From The Array const gradesTotal = grades.reduce((a, b) => a + b); const totalGrades = grades.length; const average = gradesTotal / totalGrades; return average; } // Function To See If Two Array's Are Identical (Have The Same Values) function compareArrays(a, b) { // Check If Array's Have The Same Length if (a.length !== b.length) { return false; } // Check To See If All The Values Are The Same for (let i = a.length; i--;) { if (a[i] !== b[i]) { return false; } } return true; } // Function To See If All Values In An Array Are Between 0 - 110 function isValidGrades(array) { if (array >= 0 && array <= 110) { return true; } else { return false; } } // Function To Change Grade View function setGradeView(view) { // Define All Needed DOM Elements const numberGrades = document.querySelectorAll("span.numberGrade"); const letterGrades = document.querySelectorAll("span.letterGrade"); const gpaGrades = document.querySelectorAll("span.gpaGrade"); const numAverage = document.querySelector("span#totalAverage.numberGrade"); const letterAverage = document.querySelector("span#totalAverage.letterGrade"); const gpaAverage = document.querySelector("span#totalAverage.gpaGrade"); // Use Conditionals To Show Selected Grade Type & Hide The Others if (view === "number") { localStorage.setItem("DefaultGradeView", "number"); numAverage.style.display = "initial"; letterAverage.style.display = "none"; gpaAverage.style.display = "none"; numberGrades.forEach(e => { e.style.display = "initial"; }); letterGrades.forEach(e => { e.style.display = "none"; }); gpaGrades.forEach(e => { e.style.display = "none"; }); } if (view === "letter") { localStorage.setItem("DefaultGradeView", "letter"); numAverage.style.display = "none"; letterAverage.style.display = "initial"; gpaAverage.style.display = "none"; numberGrades.forEach(e => { e.style.display = "none"; }); letterGrades.forEach(e => { e.style.display = "initial"; }); gpaGrades.forEach(e => { e.style.display = "none"; }); } if (view === "gpa") { localStorage.setItem("DefaultGradeView", "gpa"); numAverage.style.display = "none"; letterAverage.style.display = "none"; gpaAverage.style.display = "initial"; numberGrades.forEach(e => { e.style.display = "none"; }); letterGrades.forEach(e => { e.style.display = "none"; }); gpaGrades.forEach(e => { e.style.display = "initial"; }); } } // Puts The Inputted Grades Array Through Series Of Conditionals And Returns Appropriate Data For Each Grade function convertGrades(grade) { // All The Data For Each Grade Is Pushed Into This Array const finalData = []; // Loops Through Entire Inputted Array grade.forEach(a => { // This Must Be A Whole Number Or An Error May Occur If The Decimal Is In Between The Ranges Defined Below const e = Math.floor(a); // Defining All Necessary Variables let letterGrade; let gpaGrade; let gradeIcon; let gradeColor; let gradeType; // Conditionals To Find Appropriate Values Based On Grade if (e > 100) { letterGrade = "A++"; gpaGrade = 4.0; gradeIcon = "/img/ico/star.png"; gradeColor = "#0087FF"; gradeType = "Honors"; } if (e >= 97 && e <= 100) { letterGrade = "A+"; gpaGrade = 4.0; gradeIcon = "/img/ico/star.png"; gradeColor = "#0087FF"; gradeType = "Honors"; } if (e >= 93 && e <= 96) { letterGrade = "A"; gpaGrade = 4.0; gradeIcon = "/img/ico/star.png"; gradeColor = "#0087FF"; gradeType = "Honors"; } if (e >= 90 && e <= 92) { letterGrade = "A-"; gpaGrade = 3.67; gradeIcon = "/img/ico/star.png"; gradeColor = "#0087FF"; gradeType = "Honors"; } if (e >= 87 && e <= 89) { letterGrade = "B+"; gpaGrade = 3.33; gradeIcon = "/img/ico/tick.png"; gradeColor = "#1FBA24"; gradeType = "Passing"; } if (e >= 83 && e <= 86) { letterGrade = "B"; gpaGrade = 3.00; gradeIcon = "/img/ico/tick.png"; gradeColor = "#1FBA24"; gradeType = "Passing"; } if (e >= 80 && e <= 82) { letterGrade = "B-"; gpaGrade = 2.67; gradeIcon = "/img/ico/tick.png"; gradeColor = "#1FBA24"; gradeType = "Passing"; } if (e >= 77 && e <= 79) { letterGrade = "C+"; gpaGrade = 2.33; gradeIcon = "/img/ico/error.png"; gradeColor = "#AA9901"; gradeType = "Borderline"; } if (e >= 73 && e <= 76) { letterGrade = "C"; gpaGrade = 2.00; gradeIcon = "/img/ico/error.png"; gradeColor = "#AA9901"; gradeType = "Borderline"; } if (e >= 70 && e <= 72) { letterGrade = "C-"; gpaGrade = 1.67; gradeIcon = "/img/ico/error.png"; gradeColor = "#AA9901"; gradeType = "Borderline"; } if (e >= 67 && e <= 69) { letterGrade = "D+"; gpaGrade = 1.33; gradeIcon = "/img/ico/error.png"; gradeColor = "#AA9901"; gradeType = "Borderline"; } if (e >= 63 && e <= 66) { letterGrade = "D"; gpaGrade = 1.00; gradeIcon = "/img/ico/exclamation.png"; gradeColor = "#CF1920"; gradeType = "Failing"; } if (e >= 60 && e <= 62) { letterGrade = "D-"; gpaGrade = 0.67; gradeIcon = "/img/ico/exclamation.png"; gradeColor = "#CF1920"; gradeType = "Failing"; } if (e < 60) { letterGrade = "F"; gpaGrade = 0.00; gradeIcon = "/img/ico/exclamation.png"; gradeColor = "#CF1920"; gradeType = "Failing"; } // Combines All Data Per Grade Into An Object const gradeData = { "Number_Grade": a, "Letter_Grade": letterGrade, "GPA_Grade": gpaGrade, "Grade_Icon": gradeIcon, "Grade_Color": gradeColor, "Grade_Type": gradeType }; // Pushes Object Into FinalData Array finalData.push(gradeData); }); return finalData; } // Sets The Value For The Average Element // Icon, Color, & Type Can Be Automatically Set function setAverage(average, custom) { // Get Average Data By Passing Average As An Array Through ConvertGrades Function const gradeData = convertGrades([average]); // Define All Necessary Items From GradeData const gradeColor = gradeData[0].Grade_Color; const gradeType = gradeData[0].Grade_Type; const gradeIcon = gradeData[0].Grade_Icon; const letterGrade = gradeData[0].Letter_Grade; const gpaGrade = gradeData[0].GPA_Grade; // Get Default Grade View const gradeView = localStorage.getItem("DefaultGradeView"); // Only Show Grade View Which Is Default let numView = "none"; let letterView = "none"; let gpaView = "none"; if (gradeView === "number") { numView = "initial"; } if (gradeView === "letter") { letterView = "initial"; } if (gradeView === "gpa") { gpaView = "initial"; } // If Custom Is True Create New Element For Custom Average if (custom === true) { // Remove Custom Average HTML If It Exists To Replace With New HTML if (document.getElementById("customAverageParent") !== null) { document.getElementById("customAverageParent").remove(); } // Select Average Container const averageContainer = document.querySelector("span#averageContainer"); // Hide Original Average Element document.getElementById("averageParent").style.display = "none"; // Add In Custom Average HTML averageContainer.insertAdjacentHTML("beforeend", ` <a id="customAverageParent" style="display: initial; color: #585b66; position: static; float: right; padding-top: 10px; padding-right: 10px; cursor: pointer"> Total Average: <img id="customAverageIcon" src="${gradeIcon}" /> <span style="display: ${numView}; color: ${gradeColor};" data-gtype="${gradeType}" class="numberGrade" id="customTotalAverage">${average}%</span> <span style="display: ${letterView}; color: ${gradeColor};" data-gtype="${gradeType}" class="letterGrade" id="customTotalAverage">${letterGrade}</span> <span style="display: ${gpaView}; color: ${gradeColor};" data-gtype="${gradeType}" class="gpaGrade" id="customTotalAverage">${gpaGrade}</span> </a> `); } else { // Set All New Data document.querySelectorAll("span#totalAverage").forEach(e => { e.style.color = gradeColor; e.dataset.gtype = gradeType; }); document.getElementById("averageIcon").src = gradeIcon; document.querySelector("span#totalAverage.numberGrade").innerText = `${average}%`; document.querySelector("span#totalAverage.letterGrade").innerText = letterGrade; document.querySelector("span#totalAverage.gpaGrade").innerText = gpaGrade; } } // Function To Load Saved Grades function loadSavedGrades() { // Parse The Saved Grades So It Can Be Read As An Array const savedGrades = JSON.parse(localStorage.getItem("CustomGrades")); // Selects All The Grade Container Elements const gradeSpan = document.querySelectorAll("table#progress-card > tbody > tr > td > span"); // All Grades Custom Included let allGrades = []; for (let i = 0; i < savedGrades.length; i++) { if (savedGrades[i] !== null) { // Hide Original Grade gradeSpan[i].style.display = "none"; // Get Data On Custom Grade const gradeData = convertGrades([savedGrades[i]]); // Define All Necessary Items From GradeData const customNumberGrade = gradeData[0].Number_Grade; const customLetterGrade = gradeData[0].Letter_Grade; const customGpaGrade = gradeData[0].GPA_Grade; const customGradeType = gradeData[0].Grade_Type; const customGradeIcon = gradeData[0].Grade_Icon; const customGradeColor = gradeData[0].Grade_Color; // Get Default Grade View const gradeView = localStorage.getItem("DefaultGradeView"); // Only Show Grade View Which Is Default let numView = "none"; let letterView = "none"; let gpaView = "none"; if (gradeView === "number") { numView = "initial"; } if (gradeView === "letter") { letterView = "initial"; } if (gradeView === "gpa") { gpaView = "initial"; } // Add Custom Grade Span Below Original Grade Span // Set All Custom Grades HTML gradeSpan[i].insertAdjacentHTML("beforebegin", ` <span class="customGrade" style="display: initial"> <span class="numberGrade" style="display: ${numView}; color: ${customGradeColor};" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customNumberGrade} </span> <span class="letterGrade" style="display: ${letterView}; color: ${customGradeColor}" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customLetterGrade} </span> <span class="gpaGrade" style="display: ${gpaView}; color: ${customGradeColor}" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customGpaGrade} </span> </span> `); } } // Selects All Visible Grade Spans const visibleGradeSpans = document.querySelectorAll("table#progress-card > tbody > tr > td > span[style='display: initial'] > span.numberGrade"); visibleGradeSpans.forEach(e => { const grade = parseFloat(e.dataset.numbergrade); allGrades.push(grade); }); // Calculate Custom Average let customGradeAverage = calculateAverage(allGrades); // Round Custom Average customGradeAverage = Math.round(customGradeAverage * 100) / 100; // Set Custom Average setAverage(customGradeAverage, true); } // Add Menu Item To UserScript Manager // Grade Changer Function GM_registerMenuCommand("Change Grades", () => { // Only Run If Progress Card Exists if (document.getElementById("progress-card") !== null) { // NOTE FACEBOX IS A DEFAULT LIBRARY PUPILPATH USES // Setting Up Grade Changer Modal HTML jQuery.facebox(` <div id="gradeChanger"> <h2 style="text-align: center;">Grade Changer</h2> <b> <h5 id="gradesNotChanged" class="customError" style="display: none; text-align: center; padding: 5px; background-color: #f08080; border-radius: 5px;">Error: You Have Not Made Any Changes!</h5> <h5 id="gradesTooHigh" class="customError" style="display: none; text-align: center; padding: 5px; background-color: #f08080; border-radius: 5px;">Error: Grades Must Be Between 0 - 110!</h5> <h5 id="changedGrades" class="customWarning" style="display: none; text-align: center; padding: 5px; background-color: #f0eb80; border-radius: 5px;">Warning: Grades Have Been Changed!</h5> </b> <form> <a class="btn btn-danger" id="resetButton" style="margin-right: 5px; margin-top: 5px;" title="Reset all grades back to original grades">Reset</a> <a class="btn btn-warning" id="clearButton" style="margin-right: 5px; margin-top: 5px;" title="Clear all grades from inputs">Clear</a> <a class="btn btn-success" id="setButton" style="margin-right: 50px; margin-top: 5px;" title="Set all custom grades">Set</a> <a class="btn btn-danger" id="saveButton" style="float: right; margin-top: 5px;" title="Save/Delete all custom grades (survives reload)">Save</a> </form> </div> `); // Set Save Button Name To Delete If Custom Grades Exist if (localStorage.getItem("CustomGrades") !== null) { document.getElementById("saveButton").innerText = "Delete"; } // Gets All Class Name's And Grades Then Adds Them Into The Grade Changer Modal const gradeSpan = document.querySelectorAll("table#progress-card > tbody > tr > td > span.originalGrade > span.numberGrade"); // Convert NodeList To Array To Reverse Order Of Elements So They Will Be In Correct Order When The HTML Is Inserted Below const reversedGradeSpan = Array.from(gradeSpan).reverse(); // Adding The Class Name's And Grade Input HTML To The Grade Changer Modal reversedGradeSpan.forEach(e => { const origGrade = parseFloat(e.innerText); const className = e.parentElement.parentElement.parentElement.childNodes[1].innerText.toUpperCase(); document.querySelector("div#gradeChanger > form").insertAdjacentHTML("afterbegin", ` <span> <b>${className}</b> <input style="margin: 5px;" class="classGrades" type="number" step="0.01" data-originalgrade="${origGrade}" value="" min="0" max="110"> <img class="isError" style="display: none;" src="/img/ico/exclamation.png" title="Grade Must Be Between 0-110"/> <img class="isEdit" style="display: none;" src="/img/ico/pencil.png" title="This Grade Has Been Changed"/> </span> <br /> `); }); // Selects All Inputs const inputs = document.querySelectorAll("div#gradeChanger > form > span > input"); // Select All Visible Grades const visibleGrades = document.querySelectorAll("table#progress-card > tbody > tr > td > span[style='display: initial'] > span.numberGrade"); for (let i = 0; i < inputs.length; i++) { // Visible Grade const grade = visibleGrades[i].dataset.numbergrade; // Changed Grade Warning const changedGrades = document.getElementById("changedGrades"); // If Grade Was Changed Do This if (grade != inputs[i].dataset.originalgrade) { // Show Edited Icon If Grade Was Changed inputs[i].parentElement.lastElementChild.style.display = "initial"; // Show Grade Changed Warning changedGrades.style.display = "block"; } // Sets The Current Visible Grade To Fill Input inputs[i].value = grade; } // Add CSS To Document HEAD To Remove Arrows From Number Input document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeend", ` <style> input[type='number'] { -moz-appearance:textfield; } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; } </style> `); // This Is To Remove The Default 600px Width On FaceBox Element document.getElementById("gradeChanger").parentElement.parentElement.style = "width: auto"; // Button Functions For Grade Changer // Reset Button document.getElementById("resetButton").addEventListener("click", () => { // Define Necessary Variables const inputs = document.querySelectorAll("div#gradeChanger > form > span > input"); const customGradeSpan = document.querySelectorAll("span.customGrade"); const originalGradeSpan = document.querySelectorAll("span.originalGrade"); const changedGrades = document.getElementById("changedGrades"); const customAverage = document.getElementById("customAverageParent"); const originalAverage = document.getElementById("averageParent"); // Remove Custom Average Element If It Exists if (customAverage !== null) { customAverage.remove(); } // Show Original Average Element originalAverage.style.display = "initial"; // Hide Grade Changed Warning changedGrades.style.display = "none"; // Remove All Changed Grades Spans customGradeSpan.forEach(e => { e.remove(); }); // Show All Original Grades originalGradeSpan.forEach(e => { e.style.display = "initial"; }); // Set Each Input To Its Original Value inputs.forEach(e => { const origGrade = e.dataset.originalgrade; e.value = origGrade; // Hide Edited Icon e.parentElement.lastElementChild.style.display = "none"; // Hide Error Icon e.parentElement.children[2].style.display = "none"; }); }); // Clear Button document.getElementById("clearButton").addEventListener("click", () => { const inputs = document.querySelectorAll("div#gradeChanger > form > span> input"); inputs.forEach(e => { // Set Each Input To Nothing e.value = ""; // Hide Edited Icon e.parentElement.lastElementChild.style.display = "none"; // Hide Error Icon e.parentElement.children[2].style.display = "none"; }); }); // Set Button document.getElementById("setButton").addEventListener("click", () => { // Selects All The Grade Container Elements const gradeSpan = document.querySelectorAll("table#progress-card > tbody > tr > td > span.originalGrade > span.numberGrade"); // All The Original Grades Are Pushed Into This Array const allOriginalGrades = []; // All Grade Changer Inputs const inputs = document.querySelectorAll("div#gradeChanger > form > span> input"); // All Custom Grades Are Pushed Into This Array const customGrades = []; // Push All Original Grades Into Above Array gradeSpan.forEach(e => { // Make Sure To Use ParseFloat NOT ParseInt B/C ParseFloat Leaves Decimals Intact allOriginalGrades.push(parseFloat(e.innerText)); }); // Get All Custom Grades From Inputs And Push To Array inputs.forEach(e => { const grade = parseFloat(e.value); // Only Push Numbers To Array if (isNaN(grade) === false) { customGrades.push(grade); } else { customGrades.push(null); } }); // Only Continue If The Custom Grades Are Different Than The Original Grades if (compareArrays(allOriginalGrades, customGrades) === false) { // Check If Every Value Of CustomGrades Is Between 0 - 110 Using isValidGrades Function if (customGrades.every(isValidGrades) === true) { // All Of The Final Custom Grades Will Be Pushed Into This const finalCustomGrades = []; // Loop Through The Original & Custom Grades for (let i = 0; i < allOriginalGrades.length; i++) { let grade; // If The Grades Are The Same Or Null Push Null if (allOriginalGrades[i] === customGrades[i] || customGrades[i] === null) { grade = null; } // If Grades Are Different Push The Custom Grade if (allOriginalGrades[i] !== customGrades[i]) { grade = customGrades[i]; } // Push Grades To Array finalCustomGrades.push(grade); } // Loop Through The Final Custom Grades for (let i = 0; i < finalCustomGrades.length; i++) { // If The Value Is Null Hide Edited Icon // Else Show The Edited Icon if (finalCustomGrades[i] !== null) { inputs[i].parentElement.lastElementChild.style.display = "initial"; } else { inputs[i].parentElement.lastElementChild.style.display = "none"; } // Hide Error Icon inputs[i].parentElement.children[2].style.display = "none"; } // Selects All Original Grade Container Spans const originalGradeSpan = document.querySelectorAll("span.originalGrade"); // Loop Through All Custom Grade Spans for (let i = 0; i < finalCustomGrades.length; i++) { const firstGradeChild = originalGradeSpan[i].parentElement.firstElementChild; // Only Run On Classes With A Custom Grade if (finalCustomGrades[i] !== null) { const changedGrades = document.getElementById("changedGrades"); // Show Grade Changed Warning changedGrades.style.display = "block"; // Remove Custom Grade Span If It Exists if (firstGradeChild.className === "customGrade") { firstGradeChild.remove(); } // Hide Original Grade firstGradeChild.style.display = "none"; // Get Data On Custom Grade const gradeData = convertGrades([finalCustomGrades[i]]); // Define All Necessary Items From GradeData const customNumberGrade = gradeData[0].Number_Grade; const customLetterGrade = gradeData[0].Letter_Grade; const customGpaGrade = gradeData[0].GPA_Grade; const customGradeType = gradeData[0].Grade_Type; const customGradeIcon = gradeData[0].Grade_Icon; const customGradeColor = gradeData[0].Grade_Color; // Get Default Grade View const gradeView = localStorage.getItem("DefaultGradeView"); // Only Show Grade View Which Is Default let numView = "none"; let letterView = "none"; let gpaView = "none"; if (gradeView === "number") { numView = "initial"; } if (gradeView === "letter") { letterView = "initial"; } if (gradeView === "gpa") { gpaView = "initial"; } // Add Custom Grade Span Below Original Grade Span // Set All Custom Grades HTML originalGradeSpan[i].insertAdjacentHTML("beforebegin", ` <span class="customGrade" style="display: initial"> <span class="numberGrade" style="display: ${numView}; color: ${customGradeColor};" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customNumberGrade} </span> <span class="letterGrade" style="display: ${letterView}; color: ${customGradeColor}" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customLetterGrade} </span> <span class="gpaGrade" style="display: ${gpaView}; color: ${customGradeColor}" data-numbergrade="${customNumberGrade}" data-lettergrade="${customLetterGrade}" data-gpagrade="${customGpaGrade}" data-gtype="${customGradeType}"> <img src="${customGradeIcon}"> ${customGpaGrade} </span> </span> `); // Select All Visible Grades const allVisibleGradesSpans = document.querySelectorAll("span[style='display: initial'] > span.numberGrade"); // Array To Hold All Visible Grades const allVisibleGrades = []; allVisibleGradesSpans.forEach(e => { const grade = parseFloat(e.innerText); // Push All Visible Grades To Array allVisibleGrades.push(grade); }); let customGradeAverage = calculateAverage(allVisibleGrades); customGradeAverage = Math.round(customGradeAverage * 100) / 100; setAverage(customGradeAverage, true); } } } else { inputs.forEach(e => { if (e.value > 110 || e.value < 0) { e.parentElement.children[2].style.display = "initial"; } }); // Select Grades Too High Error const gradesTooHigh = document.getElementById("gradesTooHigh"); // Show Error gradesTooHigh.style.display = "block"; // Hide Error After 5 Seconds setTimeout(() => { gradesTooHigh.style.display = "none"; }, 5000); } } else { // Selects Changed Grades Warning const changedGrades = document.getElementById("changedGrades"); // Hide Grade Changed Warning changedGrades.style.display = "none"; // Selects All Grade Edited Icons const editedGradeIcon = document.querySelectorAll("img.isEdit"); // Hides All Grade Edited Icons editedGradeIcon.forEach(e => { e.style.display = "none"; }); // Selects Grades Not Changed Error const gradesNotChanged = document.getElementById("gradesNotChanged"); // Show Error gradesNotChanged.style.display = "block"; // Hide Error After 5 Seconds setTimeout(() => { gradesNotChanged.style.display = "none"; }, 5000); } }); // Save Button document.getElementById("saveButton").addEventListener("click", () => { // Selects All The Grade Container Elements const gradeSpan = document.querySelectorAll("table#progress-card > tbody > tr > td > span.originalGrade > span.numberGrade"); // All The Original Grades Are Pushed Into This Array const allOriginalGrades = []; // Push All Original Grades Into Above Array gradeSpan.forEach(e => { // Make Sure To Use ParseFloat NOT ParseInt B/C ParseFloat Leaves Decimals Intact allOriginalGrades.push(parseFloat(e.innerText)); }); // Do Correct Action Based On Button Name if (document.getElementById("saveButton").innerText === "Save") { // Change Button Name After Click document.getElementById("saveButton").innerText = "Delete"; // Selects All Grade Changer Inputs const inputs = document.querySelectorAll("div#gradeChanger > form > span> input"); // All Custom Grades Will Be Pushed Into This const customGrades = []; // Put All Custom Grades In An Array inputs.forEach(e => { customGrades.push(parseFloat(e.value)); }); // Do This If Grades Have Changed if (compareArrays(allOriginalGrades, customGrades) === false) { // All Final Custom Grades Will Be Pushed Into This const finalCustomGrades = []; // Loop Over All Grades for (let i = 0; i < allOriginalGrades.length; i++) { let grade; // If Grade Is Not Changed Push Null if (allOriginalGrades[i] === customGrades[i]) { grade = null; } // If Grade Is Different Push Custom Grade if (allOriginalGrades[i] !== customGrades[i]) { grade = customGrades[i]; } // Push Grades To Array finalCustomGrades.push(grade); } // NOTE THE ARRAY MUST BE A STRING TO SAVE TO LOCALSTORAGE THUS THE ARRAY IS CONVERTED TO A STRING WITH JSON.STRINGIFY localStorage.setItem("CustomGrades", JSON.stringify(finalCustomGrades)); } else { // Select Grades Not Changed Error const gradesNotChanged = document.getElementById("gradesNotChanged"); // Show Error gradesNotChanged.style.display = "block"; // Hide Error After 5 Seconds setTimeout(() => { gradesNotChanged.style.display = "none"; }, 5000); // Change Button Name Back To "Save" B/C No Grades Have Changed document.getElementById("saveButton").innerText = "Save"; } } else if (document.getElementById("saveButton").innerText === "Delete") { // Remove Custom Grades localStorage.removeItem("CustomGrades"); // Change Button Name To "Save" After Click document.getElementById("saveButton").innerText = "Save"; } }); } }); // Only Run If The Progress Card Exists if (document.getElementById("progress-card") !== null) { // Creating The Average Container HTML document.getElementsByClassName("notification information")[0].insertAdjacentHTML("afterbegin", ` <span id="averageContainer" title="Click For More Info"> <a id="averageParent" style="display: initial; color: #585b66; position: static; float: right; padding-top: 10px; padding-right: 10px; cursor: pointer"> Total Average: <img id="averageIcon" /> <span style="display: initial;" class="numberGrade" id="totalAverage"></span> <span style="display: none;" class="letterGrade" id="totalAverage"></span> <span style="display: none;" class="gpaGrade" id="totalAverage"></span> </a> </span> `); // Selects All The Grade Container Elements const gradeSpan = document.querySelectorAll("table#progress-card > tbody > tr > td > span"); // All The Original Grades Are Pushed Into This Array const allOriginalGrades = []; // Push All Original Grades Into Above Array gradeSpan.forEach(e => { // Make Sure To Use ParseFloat NOT ParseInt B/C ParseFloat Leaves Decimals Intact allOriginalGrades.push(parseFloat(e.innerText)); }); // Returns The Average Of All The Original Grades, In Some Cases As A Float const rawAverage = calculateAverage(allOriginalGrades); // Rounds The RawAverage In Case It Is A Float To The Hundredths Place (2 After Decimal) // NOTE DO NOT USE ParseFloat WITH ToFixed AS IT WILL LEAVE TRAILING ZEROS IF A WHOLE NUMBER IS PASSED const average = Math.round(rawAverage * 100) / 100; // Call SetAverage Function To Set The Average, With Automatic Data (Colors, Icons, etc..) setAverage(average); // Create Alternate Elements For Each Class Grade With Different Grading Standards gradeSpan.forEach(e => { // Original Grade const grade = parseFloat(e.innerText); // Get Data On Original Grade const gradeData = convertGrades([grade]); // Define All Necessary Items From GradeData const gradeColor = gradeData[0].Grade_Color; const numGrade = gradeData[0].Number_Grade; const letterGrade = gradeData[0].Letter_Grade; const gpaGrade = gradeData[0].GPA_Grade; const gradeType = gradeData[0].Grade_Type; const gradeIcon = gradeData[0].Grade_Icon; // Create The HTML For The Alternate Elements With Correct Data const numberGradeHTML = ` <span class="numberGrade" style="color: ${gradeColor}; display: initial;" data-numbergrade="${numGrade}" data-lettergrade="${letterGrade}" data-gpagrade="${gpaGrade}" data-gtype="${gradeType}"> <img src="${gradeIcon}"> ${numGrade}</span> `; const letterGradeHTML = ` <span class="letterGrade" style="display: none; color: ${gradeColor}" data-numbergrade="${numGrade}" data-lettergrade="${letterGrade}" data-gpagrade="${gpaGrade}" data-gtype="${gradeType}"> <img src="${gradeIcon}"> ${letterGrade}</span> `; const gpaGradeHTML = ` <span class="gpaGrade" style="display: none; color: ${gradeColor}" data-numbergrade="${numGrade}" data-lettergrade="${letterGrade}" data-gpagrade="${gpaGrade}" data-gtype="${gradeType}"> <img src="${gradeIcon}"> ${gpaGrade}</span> `; // Append The HTML Before The Original Grade Element e.insertAdjacentHTML("beforebegin", `<span style="display: initial" class="originalGrade">${numberGradeHTML}${letterGradeHTML}${gpaGradeHTML}</span>`); // Set The Original Grade HTML To Nothing To Remove It e.outerHTML = ``; }); // If Custom Grades Are Avaliable Run LoadSavedGrades Function if (localStorage.getItem("CustomGrades") !== null) { loadSavedGrades(); } // Set Default Grade View If It Does Not Exist if (localStorage.getItem("DefaultGradeView") === null) { localStorage.setItem("DefaultGradeView", "number"); } // Default Grade View const gradeView = localStorage.getItem("DefaultGradeView"); // Change Current View To Default Grade View setGradeView(gradeView); // Detailed Average Info On Average Container Click document.getElementById("averageContainer").addEventListener("click", () => { // Select The Average Anchor Element const averageAnchor = document.querySelectorAll("span#averageContainer > a"); // The Current Average let visibleAverage = []; averageAnchor.forEach(e => { // If Element Is Visible Get Average Value if (e.style.display == "initial") { const average = parseFloat(e.childNodes[3].innerText.replace(/[^0-9.]/g, "")); // Push Average To Array visibleAverage.push(average); } }); // Get Data On Visible Average const gradeData = convertGrades(visibleAverage); // Define All Necessary Items From GradeData const numGrade = gradeData[0].Number_Grade; const letterGrade = gradeData[0].Letter_Grade; const gpaGrade = gradeData[0].GPA_Grade; // NOTE FACEBOX IS A DEFAULT LIBRARY PUPILPATH USES // Setting Up Average Detail's Modal jQuery.facebox(` <div id="averageInfo"> <h2>Average Conversion</h2> <h4>Percent Grade: ${numGrade}%</h4> <h4>Letter Grade: ${letterGrade}</h4> <h4>GPA: ${gpaGrade}</h4> <form> <label for="numberView" style="display: initial;" title="Show all grades as numbers"> <input id="numberView" type="radio" name="gradeType" value="number"> Number Grades </label> <label for="letterView" style="display: initial;" title="Show all grades as letters"> <input id="letterView" type="radio" name="gradeType" value="letter"> Letter Grades </label> <label for="gpaView" style="display: initial;" title="Show all grades as gpa's"> <input id="gpaView" type="radio" name="gradeType" value="gpa"> GPA Grades </label> </form> </div> `); // Selects Default Grade View LocalStorage Item const gradeView = localStorage.getItem("DefaultGradeView"); // Select All Necessary Elements Through Variables const numberViewRadio = document.getElementById("numberView"); const letterViewRadio = document.getElementById("letterView"); const gpaViewRadio = document.getElementById("gpaView"); // Enable The Radio Button With The Currently Active Grade View if (gradeView === "number") { numberViewRadio.checked = true; } if (gradeView === "letter") { letterViewRadio.checked = true; } if (gradeView === "gpa") { gpaViewRadio.checked = true; } // This Is To Remove The Default 600px Width On FaceBox Element document.getElementById("averageInfo").parentElement.parentElement.style = "width: auto"; // Change Grade View Type On Click document.querySelector("div#averageInfo > form").addEventListener("click", () => { // Select Form Element const form = document.querySelector("div#averageInfo > form"); // Get User Selection From Form const data = new FormData(form); let choice; // Loop Through Form Data To Find User Choice for (const entry of data) { choice = entry[1]; } setGradeView(choice); }); }); }