您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Grabs Grades from Schoology
// ==UserScript== // @name Schoology Grades // @version 2.0 // @description Grabs Grades from Schoology // @author Hafnium780 // @match https://*.schoology.com/sg // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @license MIT // @namespace hafnium780/sg // ==/UserScript== (function () { document.title = "sg"; window.stop(); document.body = document.createElement("body"); // Make new page document.body.innerHTML = ` <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> <style> * { margin: 0px; padding: 0px; font-family: Inter; align-items: center; text-align: center; } .nosel { -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: -moz-none; -o-user-select: none; user-select: none; } input { outline: none !important } ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-track { background: #1E1F21; } ::-webkit-scrollbar-thumb { background: #363639; border-radius: 5px; } html { overflow: hidden; } body { padding: 0px; } #bar { background-color: #363639; padding: 6px; height: 65px; top: 0px; left: 0px; right: 0px; border-bottom: 2px solid #424244; } #title { color: #F5F4F3; font-size: 25px; margin: 6px 0px; font-weight: 700; } #domain { margin-top: 3px; } #domain-input { width: 50px; height: 12px; padding-bottom: -2px; font-size: 13px; border: none; border-radius: 2px; background-color: #161619; color: #F5F4F3; } #domain-text { font-size: medium; color: #F5F4F3; } #secrets { display: flex; position: absolute; height: 50px; width: 450px; transition: left ease 200ms; bottom: 0px; left: 0px; border: none; } #secrets-input { background-color: #363639; width: 435px; padding: 4px; height: 42px; border: none; border-top: 2px solid #424244; } #key { font-size: medium; padding-left: 47px; text-align: left; color: #F5F4F3; } #key-input { width: 310px; height: 14px; background-color: #161619; border: 1px solid black; font-family: monospace; font-size: small; border: none; color: #F5F4F3; } #signature { font-size: medium; padding-left: 3px; text-align: left; color: #F5F4F3; } #signature-input { width: 310px; height: 14px; background-color: #161619; border: 1px solid black; font-family: monospace; font-size: small; border: none; color: #F5F4F3; } #secrets-toggle { writing-mode: vertical-rl; text-orientation: mixed; color: #F5F4F3; width: 15px; background-color: #F9485F; height: 46px; padding: 2px; cursor: pointer; font-size: 12px; line-height: 1.25; border-top-right-radius: 10px; border-bottom-right-radius: 10px; } #grades { display: flex; align-items: start; } #course-grades { overflow: auto; width: 450px; height: calc(100vh - 65px); background-color: #2E2E30; border-right: 2px solid #424244; } .course-grade-element:first-child { margin-top: 10px; } .course-grade-element { font-size: 14px; display: flex; height: 40px; cursor: pointer; filter: brightness(100%); margin: 8px; background-color: #2E2E30; border-radius: 10px; } .course-grade-name { width: 80%; text-align: left; padding-left: 10px; color: #F5F4F3; } .course-grade-grade { width: 14%; text-align: center; padding: 5px; background-color: #E9384F; color: #F5F4F3; border-radius: 10px; margin-left: 16px; } .course-grade-element:hover { filter: brightness(100%); background-color: #5A5A5C; transition: filter ease 300ms, background-color ease 300ms; } #assignment-div { width: calc(100vw - 450px); height: calc(100vh - 65px); background-color: #1E1F21; } #assignment-grades { overflow: auto; width: 100%; height: calc(100vh - 180px); background-color: #1E1F21; } #calculated-grade-title { font-size: large; color: #F5F4F3; margin: 6px 0px; } #calculated-grade { font-size: x-large; color: #F5F4F3; padding: 5px; background-color: #E9384F; border-radius: 10px; width: min-content; margin: 6px auto; } #calculated-grade-error { font-size: small; color: #F5F4F3; margin: 6px 0px; } .categorytitlediv { border-top: 2px solid #424244; position: sticky; background-color: #2E2E30; top: 0px; display: flex; flex-direction: row; margin-bottom: 3px; padding: 5px; color: #F5F4F3; z-index: 100; } .categorypoints { font-size: small; flex: 0 0; text-align: center; padding: 5px; background-color: #E9384F; border-radius: 10px; min-width: 100px; margin-left: 10px; } .categorypercent { font-size: small; flex: 0 0; text-align: center; padding: 5px; background-color: #C1B4AE; color: #1E1F21; border-radius: 10px; min-width: 60px; } .categorytitle { font-size: large; font-weight: bold; flex: 1 1; text-align: center; position: absolute; left: 0px; right: 0px; pointer-events: none; } .categoryweight { font-size: small; padding: 5px; text-align: center; background-color: #C1B4AE; color: #1E1F21; border-radius: 10px; flex: 0 0; min-width: 100px; margin-left: 10px; margin-right: calc(100% - 423px); } .categoryweightmethod { cursor: pointer; font-size: small; padding: 5px; text-align: center; background-color: #E9384F; border-radius: 10px; flex: 0 0; min-width: 100px; transition: background-color ease 200ms; } .categoryweightmethod:hover { background-color: #F9586F; } .categoryweight:empty { opacity: 0; } .categorydiv:last-child { padding-bottom: 15px; } .assignment-grade-element { padding: 6px; padding-left: 10px; display: flex; min-width: 0; flex-shrink: 0; flex-grow: 0; align-items: center; margin-right: 2px; border-top: 2px solid #424244; } .assignment-grade-element:first-child { border: none; } .assignment-grade-input { width: 55px; background-color: #931022; font-size: 13px; color: #F5F4F3; border: none; padding: 4px 3px 4px 4px; height: 18px; flex: 0 0 55px; text-align: right; border-radius: 5px; } .assignment-grade-input.assignment-grade-input:focus { background-color: #531012 } .assignment-grade-input:hover { background-color: #731012 } ::placeholder { color: #F5F4F3; font-size: 13px; } .assignment-grade-max { flex: 0 0 52px; text-align: left; color: #F5F4F3; font-size: 13px; } .assignment-grade-grade { display: flex; align-items: center; justify-content: end; font-size: small; text-align: right; flex-grow: 0; flex-shrink: 0; vertical-align: middle; text-align: center; padding-right: 10px; padding: 5px; background-color: #931022; border-radius: 10px; width: 100px; height: 16px; overflow: hidden; } a.assignment-grade-name { color: #F5F4F3 !important; } .assignment-grade-name { color: #F5F4F3; text-align: left; flex-grow: 1; margin-right: 3px; margin-left: 3px; font-size: medium; font-weight: normal; } .assignment-grade-reset { width: 16px; height: 16px; background-color: #E9384F; flex: 0 0 16px; margin-right: 10px; cursor: pointer; scale: 1; border-radius: 7px; color: #F5F4F3 } .assignment-grade-reset:hover { scale: 1.1; transition: scale ease 100ms; } .assignment-grade-reload { border-radius: 8px; width: 16px; height: 16px; font-size: 12px; background-color: #767679; flex: 0 0 16px; left: 2px; text-align: center; color: #F5F4F3; margin: 0px 2px; cursor: pointer; scale: 1; padding: auto; } .assignment-grade-reload:hover { scale: 1.1; transition: scale ease 100ms; } </style> <div id="bar" class="nosel"> <div id="title"></div> <div id="domain"> <span id="domain-text">Domain: </span> <input type="text" id="domain-input"></input> <span id="domain-text">.schoology.com</span> </div> </div> <div id="secrets" class="nosel"> <div id="secrets-input"> <div id="key"> Key: <input id="key-input"></input> </div> <div id="signature"> Signature: <input id="signature-input"></input> </div> </div> <div id="secrets-toggle">API</div> </div> <div id="grades"> <div id="course-grades" class="nosel"> </div> <div id="assignment-div"> <div id="assignment-grades"> </div> </div> </div> `; const protocol = "https"; let grades, courses, assignments, assignmentNames, assignmentFactors, categoryTypes; let assignmentShowID = undefined; const title = document.getElementById("title"); const setTitle = (t) => { title.innerText = t; }; // Get DOM elements const domain_input = document.getElementById("domain-input"); const secrets = document.getElementById("secrets"); const key_input = document.getElementById("key-input"); const signature_input = document.getElementById("signature-input"); const secrets_toggle = document.getElementById("secrets-toggle"); const course_grades = document.getElementById("course-grades"); const assignment_div = document.getElementById("assignment-div"); const assignment_grades = document.getElementById("assignment-grades"); // Before loading grades const authError = () => { setTitle("↙ Enter API Key and Signature"); }; const start = () => { if (!key || !signature) { authError(); return; } setTitle("Loading Grades..."); loadGrades(); }; // Load and save input values const storeAssignmentDetails = () => { GM_setValue("sg_assignment_names", assignmentNames); GM_setValue("sg_assignment_factors", assignmentFactors); }; const storeCategoryDetails = () => { GM_setValue("sg_category_types", categoryTypes); }; let key = GM_getValue("sg_key"); let signature = GM_getValue("sg_signature"); let domain = GM_getValue("sg_domain"); if (domain) domain_input.value = domain; else { domain = new URL(window.location.href).host.split(".")[0]; GM_setValue("sg_domain", domain); domain_input.value = domain; } domain_input.addEventListener("input", () => { GM_setValue("sg_domain", domain_input.value); domain = domain_input.value; }); if (key) key_input.value = key; if (signature) signature_input.value = signature; if (key && signature) secrets.style.left = "-435px"; key_input.addEventListener("input", () => { GM_setValue("sg_key", key_input.value); key = key_input.value; start(); }); signature_input.addEventListener("input", () => { GM_setValue("sg_signature", signature_input.value); signature = signature_input.value; start(); }); secrets_toggle.addEventListener("click", () => { if (secrets.style.left === "-435px") secrets.style.left = "0px"; else secrets.style.left = "-435px"; }); const authHeader = () => { return ( `OAuth realm="Schoology API",oauth_consumer_key="` + key + `",oauth_token="",oauth_nonce="` + Math.floor(Math.random() * 100000000) + `",oauth_timestamp="` + Math.floor(Date.now() / 1000) + `",oauth_signature_method="PLAINTEXT",oauth_version="1.0",oauth_signature="` + signature + `%26"` ); }; const schoologyHeaders = (fileType) => { return { Accept: fileType, Host: "api.schoology.com", Authorization: authHeader(), "Content-Type": "text/xml", }; }; const setHeaders = (req, fileType) => { req.setRequestHeader("Accept", fileType); req.setRequestHeader("Host", "api.schoology.com"); req.setRequestHeader("Authorization", authHeader()); }; // API helper functions const getContent = (a, xml = false) => { return new Promise((res, rej) => { let link = a.indexOf(protocol + "://api.schoology.com") === -1 ? protocol + "://api.schoology.com/v1/" + a : a; GM_xmlhttpRequest({ url: link, method: "GET", headers: schoologyHeaders(xml ? "text/xml" : "application/json"), onload: (r) => { res(xml ? r.responseText : JSON.parse(r.responseText)); }, onerror: (e) => { rej(e); }, }); }); }; const getHTML = (a) => { return new Promise((res, rej) => { GM_xmlhttpRequest({ url: a, method: "GET", headers: schoologyHeaders("text/html"), onload: (r) => { res(r.responseText); }, onerror: (e) => { rej(e); }, }); }); }; const multiGet = (b) => { return new Promise((res, rej) => { GM_xmlhttpRequest({ url: protocol + "://api.schoology.com/v1/multiget", method: "POST", headers: schoologyHeaders("application/json"), data: b, onload: (r) => { res(JSON.parse(r.responseText)); }, onerror: (e) => { rej(e); }, }); }); }; const roundGrade = (g) => { if (g && !isNaN(g)) return g.toFixed(2) + "%"; return "N/A%"; }; const formatGrade = (a) => { return (a.grade ?? "---") + "/" + (a.max_points ?? "---"); }; // Custom display elements const createCourseGrade = (name, grade, courseI) => { let mainDiv = document.createElement("div"); mainDiv.classList.add("course-grade-element"); let nameDiv = document.createElement("div"); nameDiv.innerText = name; nameDiv.classList.add("course-grade-name"); let gradeDiv = document.createElement("div"); gradeDiv.innerText = grade; gradeDiv.classList.add("course-grade-grade"); mainDiv.appendChild(gradeDiv); mainDiv.appendChild(nameDiv); course_grades.appendChild(mainDiv); mainDiv.addEventListener("click", () => { loadAssignments(courses[courseI]); }); }; const createAssignmentGrade = (name, grade, url = undefined) => { let mainDiv = document.createElement("div"); mainDiv.classList.add("assignment-grade-element"); let nameDiv = document.createElement(url ? "a" : "div"); nameDiv.innerText = name; nameDiv.classList.add("assignment-grade-name"); if (url) nameDiv.href = url; let gradeDiv = document.createElement("div"); gradeDiv.innerText = grade; gradeDiv.classList.add("assignment-grade-grade"); mainDiv.appendChild(gradeDiv); mainDiv.appendChild(nameDiv); assignment_grades.appendChild(mainDiv); }; // Main loading and display const displayGrades = () => { course_grades.innerHTML = ""; for (const grade of grades) { createCourseGrade(grade.courseName, grade.grade, grade.courseI); } }; const displayAssignments = (s, id) => { if (assignmentShowID !== id) return; categoryTypes = {}; try { categoryTypes = GM_getValue("sg_category_types") ?? {}; } catch (e) {} assignment_grades.innerHTML = ""; let calculated_grade_title = document.getElementById("calculated-grade-title") ?? document.createElement("div"); let calculated_grade = document.getElementById("calculated-grade") ?? document.createElement("div"); let calculated_grade_error = document.getElementById("calculated-grade-error") ?? document.createElement("div"); calculated_grade_title.id = "calculated-grade-title"; calculated_grade.id = "calculated-grade"; calculated_grade_error.id = "calculated-grade-error"; calculated_grade_title.innerText = "Calculated Grade"; calculated_grade_error.innerText = "Category grades not matching with Schoology? Try switching the weighting method (Percentage or Points) for individual categories."; assignment_div.insertBefore(calculated_grade_title, assignment_grades); assignment_div.insertBefore(calculated_grade, assignment_grades); assignment_div.insertBefore(calculated_grade_error, assignment_grades); const categories = []; if (!s.grading_category[s.grading_category.length - 1].created) { s.grading_category.push({ id: undefined, title: "Other", created: true, }); } let totalWeight = 0; for (const gc of s.grading_category) { if (gc.weight) totalWeight += gc.weight; } for (let i = 0; i < s.grading_category.length; i++) { const gc = s.grading_category[i]; let weight = undefined; if (totalWeight === 100) { if (s.grading_category[i].weight) weight = s.grading_category[i].weight / totalWeight; else weight = 0; } const div = document.createElement("div"); const title = document.createElement("div"); const titlediv = document.createElement("div"); const wdiv = document.createElement("div"); const methoddiv = document.createElement("div"); wdiv.classList.add("categoryweight"); wdiv.innerText = weight ? "Weight: " + +parseFloat(weight * 100).toFixed(2) + "%" : ""; methoddiv.classList.add("categoryweightmethod"); methoddiv.classList.add("nosel"); methoddiv.innerText = (categoryTypes[gc.id] ?? 2) == 1 ? "Percentage" : "Points"; let id = gc.id; methoddiv.addEventListener("click", () => { categoryTypes[id] = (categoryTypes[gc.id] ?? 2) == 1 ? 2 : 1; methoddiv.innerText = categoryTypes[gc.id] == 1 ? "Percentage" : "Points"; storeCategoryDetails(); updateGrades(s, categories); }); title.classList.add("categorytitle"); titlediv.classList.add("categorytitlediv"); div.classList.add("categorydiv"); div.style.zIndex = i.toString(); title.innerText = gc.title; assignment_grades.appendChild(div); div.appendChild(titlediv); titlediv.appendChild(methoddiv); titlediv.appendChild(wdiv); titlediv.appendChild(title); categories.push({ id: gc.id, title: gc.title, div: div, weight: weight, totPts: 0, maxPts: 0, }); } for (let i = 0; i < assignments.length; i++) { const a = assignments[i]; const div = document.createElement("div"); const grade = document.createElement("div"); const gradeMax = document.createElement("div"); const gradeInput = document.createElement("input"); const resetGrade = document.createElement("div"); const reloadName = document.createElement("div"); let assignmentName; if (a.url) { assignmentName = document.createElement("a"); assignmentName.href = a.url; assignmentName.target = "_blank"; } else { assignmentName = document.createElement("div"); } div.classList.add("assignment-grade-element"); grade.classList.add("assignment-grade-grade"); gradeMax.classList.add("assignment-grade-max"); gradeInput.classList.add("assignment-grade-input"); resetGrade.classList.add("assignment-grade-reset"); assignmentName.classList.add("assignment-grade-name"); reloadName.classList.add("assignment-grade-reload"); reloadName.innerText = String.fromCodePoint(8635); // ↻ gradeMax.innerText = "/ " + (a.rawGrade.max ?? "---"); gradeInput.value = a.rawGrade.pts ?? ""; gradeInput.setAttribute("placeholder", "---"); resetGrade.innerText = String.fromCodePoint(8635); // ↻ if (a.assignmentName == undefined || a.assignmentName == "le>") assignmentName.innerText = "unknown"; else assignmentName.innerText = a.assignmentName; const cat = categories.find((e, i) => { return e.id === a.category_id || i == categories.length - 1; }); cat.div.appendChild(div); reloadName.addEventListener("click", () => { assignmentName.innerText = ""; getAssignmentTitles([a], s, id, true); }); gradeInput.addEventListener("input", () => { updateGrades(s, categories); }); resetGrade.addEventListener("click", () => { gradeInput.value = a.rawGrade.pts ?? ""; updateGrades(s, categories); }); div.appendChild(reloadName); div.appendChild(assignmentName); div.appendChild(resetGrade); div.appendChild(grade); grade.appendChild(gradeInput); grade.appendChild(gradeMax); a.gradeInput = gradeInput; a.resetGrade = resetGrade; } updateGrades(s, categories); // for (const assignment of assignments) { // createAssignmentGrade(assignment.assignmentName, assignment.grade, assignment.url); // } }; const updateGrades = (s, categories, reset = false) => { for (const c of categories) { c.totPts = 0; c.maxPts = 0; c.calculation_type = categoryTypes[c.id] ?? 2; } for (let i = 0; i < assignments.length; i++) { const a = assignments[i]; const cat = categories.find((e, i) => { return e.id === a.category_id || i == categories.length - 1; }); const pts = reset ? a.rawGrade.pts : parseFloat(a.gradeInput.value); if (reset) a.gradeInput.value = a.rawGrade.pts ?? ""; if (!isNaN(pts) && pts !== null) { if (cat.calculation_type == 2) { cat.totPts += pts * a.factor; cat.maxPts += a.rawGrade.max * a.factor; } else { cat.totPts += (pts / a.rawGrade.max) * a.factor; cat.maxPts += a.factor; } } if ( (isNaN(pts) && a.rawGrade.pts !== null) || (!isNaN(pts) && pts !== a.rawGrade.pts) ) { a.resetGrade.style.display = "block"; } else { a.resetGrade.style.display = "none"; } } let calcGrade; if (categories[0].weight !== undefined) { let totPer = 0; let outOf = 1; for (const gc of categories) { if (gc.maxPts === 0) outOf -= gc.weight; } for (const gc of categories) { if (gc.maxPts !== 0) { totPer += ((gc.weight / outOf) * gc.totPts) / gc.maxPts; } } calcGrade = totPer; } else { let totPts = 0, maxPts = 0; for (const gc of categories) { totPts += gc.totPts; maxPts += gc.maxPts; } calcGrade = totPts / maxPts; } for (let i = 0; i < s.grading_category.length; i++) { const points = categories[i].div.firstElementChild.getElementsByClassName( "categorypoints" )[0] ?? document.createElement("div"); const percent = categories[i].div.firstElementChild.getElementsByClassName( "categorypercent" )[0] ?? document.createElement("div"); points.innerText = (Number.isInteger(categories[i].totPts) ? categories[i].totPts : categories[i].totPts.toFixed(2)) + "/" + (Number.isInteger(categories[i].maxPts) ? categories[i].maxPts : categories[i].maxPts.toFixed(2)); percent.innerText = roundGrade( (categories[i].totPts / categories[i].maxPts) * 100 ); points.classList.add("categorypoints"); percent.classList.add("categorypercent"); categories[i].div.firstElementChild.appendChild(percent); categories[i].div.firstElementChild.appendChild(points); } document.getElementById("calculated-grade").innerText = roundGrade( calcGrade * 100 ); }; const loadGrades = async () => { grades = []; courses = []; getContent("app-user-info") .then(async (v) => { const apiID = v.api_uid; getContent("users/" + apiID + "/grades").then(async (v) => { for (const s of v.section) { courses.push(s); } let request = `<?xml version="1.0" encoding="utf-8" ?><requests>`; for (const s of v.section) { request += "<request>/v1/sections/" + s.section_id + "</request>"; } request += "</requests>"; await multiGet(request) .then((r) => { for (let i = 0; i < r.response.length; i++) { const res = r.response[i]; if (res.response_code === 403) continue; else { courses[i].courseName = res.body.course_title + " " + res.body.section_title; grades.push({ courseName: res.body.course_title + " " + res.body.section_title, grade: roundGrade(v.section[i].final_grade[0].grade), courseI: i, }); } } displayGrades(); }) .catch((e) => console.log(e)); setTitle("Select a Course"); }); }) .catch((e) => { if (e === 401) { authError(); } else console.log(e); }); }; const loadAssignments = async (s) => { setTitle("Loading " + s.courseName + "..."); const thisID = Math.floor(Math.random() * 1000000); assignmentShowID = thisID; assignments = []; assignmentNames = {}; assignmentFactors = {}; try { assignmentNames = GM_getValue("sg_assignment_names") ?? {}; assignmentFactors = GM_getValue("sg_assignment_factors") ?? {}; } catch (e) {} for (const p of s.period) { p.assignment.sort((a, b) => b.timestamp - a.timestamp); for (const a of p.assignment) { a.assignmentI = assignments.length; a.web_url = a.web_url?.replace("https://app", "https://" + domain); if (a.web_url && assignmentNames[a.web_url]) { assignments.push({ grade: formatGrade(a), assignmentName: assignmentNames[a.web_url], url: a.web_url, category_id: a.category_id, assignmentI: a.assignmentI, factor: assignmentFactors[a.web_url] ?? 1, rawGrade: { pts: a.grade, max: a.max_points }, }); a.skip = true; } else { assignments.push({ grade: formatGrade(a), assignmentName: "---", category_id: a.category_id, assignmentI: a.assignmentI, url: a.web_url, factor: assignmentFactors[a.web_url] ?? 1, rawGrade: { pts: a.grade, max: a.max_points }, }); a.skip = false; } } displayAssignments(s, thisID); const requests = []; for (let i = 0; i < p.assignment.length; i += 45) { requests.push(p.assignment.slice(i, i + 45)); } for (const r of requests) { let request = `<?xml version="1.0" encoding="utf-8" ?><requests>`; const actualI = []; for (let i = 0; i < r.length; i++) { if (r[i].skip != true) { actualI.push(i); request += `<request>` + r[i].location + `</request>`; } } request += `</requests>`; if (actualI.length === 0) continue; const rereq = []; await multiGet(request) .then((v) => { for (let i = 0; i < v.response.length; i++) { let ai = actualI[i]; const res = v.response[i]; const url = r[ai].web_url; if (res.response_code === 403) { rereq.push({ assignmentI: r[ai].assignmentI, url: url }); } else { assignments[r[ai].assignmentI].assignmentName = res.body.title; if (res.body.factor) { assignments[r[ai].assignmentI].factor = parseFloat( res.body.factor ); assignmentFactors[url] = parseFloat(res.body.factor); } assignments[r[ai].assignmentI].url = url; assignmentNames[url] = res.body.title; } } }) .catch((e) => console.log(e)); displayAssignments(s, thisID); storeAssignmentDetails(); await getAssignmentTitles(rereq, s, thisID); } } if (thisID == assignmentShowID) { setTitle(s.courseName); } }; const getAssignmentTitles = (as, s, id, force = false) => { return new Promise(async (res, rej) => { if (as.length === 0) { res(); return; } for (const a of as) { if (id !== assignmentShowID) break; if (assignmentNames[a.url] && !force) { assignments[a.assignmentI].assignmentName = assignmentNames[a.url]; assignments[a.assignmentI].url = a.url; } else { await getHTML(a.url) .then((v) => { let i = v.search(/<title>/); let j = v.search(/<\/title>/); if (i === -1) { assignments[a.assignmentI].url = a.url; } else { const tmp = document.createElement("textarea"); tmp.innerHTML = v.substring(i + 7, j - 12); assignments[a.assignmentI].assignmentName = tmp.value; assignmentNames[a.url] = tmp.value; assignments[a.assignmentI].url = a.url; storeAssignmentDetails(); displayAssignments(s, id); tmp.remove(); } }) .catch((e) => {}); } } storeAssignmentDetails(); displayAssignments(s, id); res(); }); }; start(); })();