Created
April 25, 2023 04:12
-
-
Save GalvinGao/8860d3130c316f7abdf443a577383b41 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Canvas All Info | |
// @namespace https://theusaf.github.io | |
// @version 1.3.1 | |
// @icon https://canvas.instructure.com/favicon.ico | |
// @copyright 2020-2021, Daniel Lau | |
// @license MIT | |
// @description Place all information on a single page (https://canvas.example.com/all or https://example.instructure.com/all) | |
// @author theusaf | |
// @include /^https:\/\/canvas\.[a-z0-9]*?\.[a-z]*?\/all\/?(\?.*)?$/ | |
// @include /^https:\/\/[a-z0-9]*?\.instructure\.com\/all\/?(\?.*)?$/ | |
// @grant none | |
// ==/UserScript== | |
/* | |
Note: | |
- This userscript uses public APIs accessed by canvas | |
- Gets class information | |
- Gets assignments | |
- Gets basic teacher information | |
- This userscript does not store or upload any information gathered by the script | |
- This userscript overwrites /all in canvas | |
- This userscript was originally developed for Oregon State University | |
*/ | |
/* Useful Links (for use later?) | |
/api/v1/conversations?scope=inbox&filter_mode=and&include_private_conversation_enrollments=false | |
- Canvas mail | |
/api/v1/conversations/(mailbox_id)?include_participant_contexts=false&include_private_conversation_enrollments=false | |
- Specific mail | |
/courses/(class_id)/modules/items/assignment_info | |
- Module items | |
*/ | |
/** | |
* log - logs info to console | |
* @param {*} str | |
* @param {...any} data | |
*/ | |
const log = (str, ...data) => { | |
if (data.length > 0) { | |
console.log(`[CANVAS-ALL] ${str}`, data); | |
} | |
console.log(`[CANVAS-ALL] ${str}`); | |
}; | |
/** | |
* load - Loads everything | |
*/ | |
async function load() { | |
document.title = "Dashboard - All"; | |
/** | |
* mainElement - the main application div | |
* iFrameLoader - The div for loading iframes for getting data | |
* styles - A style element | |
*/ | |
const mainElement = document.getElementById("main"), | |
iFrameLoader = document.createElement("div"); | |
mainElement.innerHTML = `<style> | |
#canvas-all-iframe-loader{ | |
visibility: hidden; | |
position: fixed; | |
width: 100%; | |
height: 100%; | |
pointer-events: none; | |
left: 0; | |
} | |
#canvas-all-iframe-loader > iframe{ | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
} | |
#main{ | |
display: flex; | |
flex-flow: column; | |
padding: 1rem; | |
} | |
#main>span{ | |
flex: 0; | |
margin-bottom: 1rem; | |
} | |
#main>div{ | |
flex: 1; | |
} | |
#canvasall_main_wrapper{ | |
display: flex; | |
} | |
#canvasall_main_wrapper>div{ | |
flex: 75%; | |
} | |
#canvasall_class_grades{ | |
display: flex; | |
flex-flow: column; | |
background: #111; | |
padding: 0.5rem; | |
border-radius: 0.5rem; | |
} | |
#canvasall_assignments_wrapper{ | |
padding: 0.5rem; | |
background: #111; | |
border-radius: 0.5rem; | |
} | |
#canvasall_main_wrapper>#canvasall_announcement_wrapper{ | |
flex: 25%; | |
padding: 0.5rem; | |
} | |
#canvasall_assignment_filter_list_chosen{ | |
display: inline-block; | |
} | |
#canvasall_assignment_filter_list_chosen>option{ | |
display: inline-block; | |
background: grey; | |
color: white; | |
padding: 0.25rem; | |
margin: 0.25rem; | |
cursor: pointer; | |
} | |
#canvasall_assignment_filter_list_chosen>option::before{ | |
content: "x "; | |
} | |
.canvasall_announcement_wrapper{ | |
margin-bottom: 0.5rem; | |
padding: 0.5rem; | |
border-radius: 0.5rem; | |
} | |
.canvasall_announcement_wrapper:nth-child(2n+1){ | |
background: rgba(255,255,255,.1); | |
} | |
.canvasall_announcement_wrapper:nth-child(2n){ | |
background: #111; | |
} | |
.canvasall_announcement_title{ | |
font-size: 1.25rem; | |
font-weight: bold; | |
} | |
.canvasall_announcement_class, | |
.canvasall_announcement_when{ | |
font-size: 0.75rem; | |
} | |
.canvasall_announcement_class>a{ | |
color: grey; | |
} | |
.canvasall_class_grade_wrapper:nth-child(2n+1){ | |
background: rgba(0,0,0,0.5); | |
} | |
.canvasall_class_grade_wrapper:nth-child(2n){ | |
background: rgba(255,255,255,.1); | |
} | |
.canvasall_class_grade_wrapper{ | |
flex: 1; | |
display: flex; | |
border-radius: 0.5rem; | |
} | |
.canvasall_class_grade_wrapper>div{ | |
flex: 1; | |
padding: 0.5rem; | |
word-break: break-all; | |
} | |
.canvasall_assignment_wrapper:nth-child(2n+1){ | |
background: transparent; | |
} | |
.canvasall_assignment_wrapper:nth-child(2n){ | |
background: rgba(255,255,255,.1); | |
} | |
.canvasall_assignment_wrapper{ | |
display: flex; | |
padding: 0.5rem; | |
border-radius: 0.5rem; | |
} | |
.canvasall_assignment_wrapper>div{ | |
flex: 1; | |
padding-right: 0.25rem; | |
padding-left: 0.25rem; | |
} | |
.canvasall_assignment_title{ | |
display: flex; | |
align-items: center; | |
} | |
.canvasall_assignment_title>img{ | |
height: 1.5rem; | |
width: 1.5rem; | |
margin-right: 0.5rem; | |
} | |
.canvasall_assignment_icon { | |
min-height: 1.5rem; | |
min-width: 1.5rem; | |
margin-right: 0.5rem; | |
} | |
.canvasall_assignment_type_assignment:before { | |
content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMCIgeT0iMCIgdmlld0JveD0iMCAwIDI4MCAyNTkiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI4MCAyNTkiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik03My4zMSwxOThjLTExLjkzLDAtMjIuMjIsOC0yNCwxOC43M2EyNi42NywyNi42NywwLDAsMC0uMywzLjYzdi4zYTIyLDIyLDAsMCwwLDUuNDQsMTQuNjUsMjIuNDcsMjIuNDcsMCwwLDAsMTcuMjIsOEgyMDBWMjI4LjE5aC0xMzRWMjEzLjA4SDIwMFYxOThabTIxLTEwNS43NGg5MC42NFY2Mkg5NC4zWk03OS4xOSwxMDcuMzRWNDYuOTJIMjAwdjYwLjQyWm03LjU1LDMwLjIxVjEyMi40NUgxOTIuNDl2MTUuMTFaTTcxLjY1LDE2LjcxQTIyLjcyLDIyLjcyLDAsMCwwLDQ5LDM5LjM2VjE5MC44OGE0MS4xMiw0MS4xMiwwLDAsMSwyNC4zMi04aDE1N1YxNi43MVpNMzMuODgsMzkuMzZBMzcuNzgsMzcuNzgsMCwwLDEsNzEuNjUsMS42SDI0NS4zNlYxOThIMjE1LjE1djQ1LjMyaDIyLjY2VjI1OC40SDcxLjY1YTM3Ljg1LDM3Ljg1LDAsMCwxLTM3Ljc2LTM3Ljc2WiI+PC9wYXRoPjwvc3ZnPgo="); | |
filter: invert(1); | |
} | |
.canvasall_assignment_type_quiz:before { | |
content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMCIgeT0iMCIgdmlld0JveD0iMCAwIDI4MCAyODAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI4MCAyODAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik05MS43MiwxMjAuNzVoOTYuNTZWMTA0LjY1SDkxLjcyWm0wLDQ4LjI4aDgwLjQ3VjE1Mi45NEg5MS43MlptMC05Ni41Nmg4MC40N1Y1Ni4zN0g5MS43MlptMTYwLjk0LDM0Ljg4SDIyOC41MlYxMC43OGgtMTc3djk2LjU2SDI3LjM0QTI0LjE3LDI0LjE3LDAsMCwwLDMuMiwxMzEuNDhWMjQ0LjE0YTI0LjE3LDI0LjE3LDAsMCwwLDI0LjE0LDI0LjE0SDI1Mi42NmEyNC4xNywyNC4xNywwLDAsMCwyNC4xNC0yNC4xNFYxMzEuNDhBMjQuMTcsMjQuMTcsMCwwLDAsMjUyLjY2LDEwNy4zNFptMCwxNi4wOWE4LjA2LDguMDYsMCwwLDEsOCw4djUxLjc3bC0zMi4xOSwxOS4zMVYxMjMuNDRaTTY3LjU4LDIwMy45MXYtMTc3SDIxMi40MnYxNzdaTTI3LjM0LDEyMy40NEg1MS40OHY3OS4xM0wxOS4yOSwxODMuMjZWMTMxLjQ4QTguMDYsOC4wNiwwLDAsMSwyNy4zNCwxMjMuNDRaTTI1Mi42NiwyNTIuMTlIMjcuMzRhOC4wNiw4LjA2LDAsMCwxLTgtOFYyMDJsMzAsMThIMjMwLjc1bDMwLTE4djQyLjEyQTguMDYsOC4wNiwwLDAsMSwyNTIuNjYsMjUyLjE5WiI+PC9wYXRoPjwvc3ZnPgo="); | |
filter: invert(1); | |
} | |
.canvasall_assignment_type_discussion_topic:before { | |
content: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMWVtIiBoZWlnaHQ9IjFlbSIgcm90YXRlPSIwIiBzdHlsZT0iaGVpZ2h0OjFlbTt3aWR0aDoxZW0iIHZpZXdCb3g9IjAgMCAxOTIwIDE5MjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnPgo8cGF0aCBkPSJtNjc3LjY1IDE2djMzOC45NGgxMTIuOTR2LTIyNS44OGgxMDE2LjV2NzkwLjQ4aC0yMjZ2MjU5Ljc2bC0yNTkuNjUtMjU5Ljc2aC03OS4xNzJ2LTQ1MS42NWgtNTY0LjU5LTY3Ny42NXYxMDE2LjVoMzM4LjcxdjQxOC45bDQxOC00MTguOWg0ODUuNTN2LTQ1MS44OGgzMi43NTNsNDE5LjEyIDQxOS4xMnYtNDE5LjEyaDIyNS44OHYtMTAxNi41aC0xMjQyLjR6bS0zMzguODYgOTAzLjU2aDU2NC43MXYtMTEyLjk0aC01NjQuNzF2MTEyLjk0em0wIDIyNS44OGgzMzguOTR2LTExMy4wNWgtMzM4Ljk0djExMy4wNXptLTIyNS44NS01NjQuNzRoMTAxNi41djc5MC43aC00MTkuMDFsLTI1OC43NSAyNTkuNjV2LTI1OS42NWgtMzM4Ljcxdi03OTAuN3oiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgo8L2c+Cjwvc3ZnPgo=") | |
filter: invert(1); | |
} | |
.canvasall_status_submit, | |
.canvasall_status_submit a{ | |
color: green; | |
} | |
.canvasall_status_done{ | |
text-decoration: line-through; | |
} | |
.canvasall_status_late{ | |
background: orange !important; | |
color: white; | |
} | |
.canvasall_status_late a{ | |
color: white; | |
} | |
.canvasall_status_missing{ | |
background: red !important; | |
color: white; | |
} | |
.canvasall_status_missing a{ | |
color: white; | |
} | |
.canvasall_status_feedback>.canvasall_assignment_title::after{ | |
content: " (Feedback available)" | |
} | |
</style> | |
<span id="canvasall_fetching_information">Fetching information... please wait...</span> | |
<div id="canvasall_main_wrapper"> | |
<div> | |
<div id="canvasall_class_wrapper"> | |
<!-- Courses, Grades, etc. --> | |
<h3>Courses</h3> | |
<div id="canvasall_class_grades"> | |
<div id="canvasall_class_grade_header" class="canvasall_class_grade_wrapper"> | |
<div><span>Class</span></div> | |
<div><span>Grade</span></div> | |
<div><span>Professor</span></div> | |
</div> | |
</div> | |
</div> | |
<h3>Current Assignments</h3> | |
<div> | |
<span>Filters:</span> | |
<select id="canvasall_assignment_filter_status"> | |
<option value="">Select</option> | |
<option value="submit">Hide Submitted</option> | |
<option value="done">Hide Graded</option> | |
<option value="late">Hide Late</option> | |
<option value="missing">Hide Missing</option> | |
<option value="quiz">Hide Quiz</option> | |
<option value="assignment">Hide Assignment</option> | |
<option value="calendar_event">Hide Calendar Event</option> | |
</select> | |
<div id="canvasall_assignment_filter_list_chosen"> | |
</div> | |
</div> | |
<div id="canvasall_assignments_wrapper"> | |
<div class="canvasall_assignment_wrapper"> | |
<div> | |
<span>Assignment Name</span> | |
</div> | |
<div> | |
<span>Class</span> | |
</div> | |
<div> | |
<span>Due Date</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="canvasall_announcement_wrapper"> | |
<h3>Announcements</h3> | |
</div> | |
</div>`; | |
iFrameLoader.id = "canvas-all-iframe-loader"; | |
mainElement.append(iFrameLoader); | |
/** | |
* courses - Class information | |
* classAssignments - Assignments for courses | |
* mainFrame - The main iframe | |
* ENV - Global variables with useful data | |
* currentUserID - The current user id | |
*/ | |
log("Getting courses"); | |
const courses = await getCourses(), | |
courseAssignments = [], | |
courseGrades = {}, | |
{ ENV } = window, | |
{ currentUserID } = ENV, | |
courseGradeDiv = mainElement.querySelector("#canvasall_class_grades"), | |
assignmentsDiv = mainElement.querySelector("#canvasall_assignments_wrapper"), | |
announcementsDiv = mainElement.querySelector( | |
"#canvasall_announcement_wrapper" | |
), | |
statusFilter = mainElement.querySelector( | |
"#canvasall_assignment_filter_status" | |
), | |
statusFilterList = mainElement.querySelector( | |
"#canvasall_assignment_filter_list_chosen" | |
), | |
localStorageConfigStr = localStorage.canvasAllConfig ?? "{}", | |
localStorageConfig = JSON.parse(localStorageConfigStr); | |
function hideType(value) { | |
if (value === "") { | |
// ignore reset to empty value | |
return; | |
} | |
const elem = statusFilter.querySelector( | |
`option[value="${value}"]` | |
), | |
temp = document.createElement("template"), | |
now = `${Math.random()}`.substr(2); | |
if (!elem) {return;} | |
temp.innerHTML = `<style id="canvasall_filter_${now}"> | |
.canvasall_status_${value}{ | |
display: none; | |
} | |
</style>`; | |
document.body.append(temp.content.cloneNode(true)); | |
statusFilterList.append(elem); | |
localStorageConfig[value] = true; | |
localStorage.canvasAllConfig = JSON.stringify(localStorageConfig); | |
statusFilter.value = ""; | |
function click() { | |
localStorageConfig[value] = false; | |
localStorage.canvasAllConfig = JSON.stringify(localStorageConfig); | |
// remove style | |
statusFilter.append(elem); | |
document.querySelector(`#canvasall_filter_${now}`).remove(); | |
statusFilter.value = ""; | |
elem.removeEventListener("click", click); | |
} | |
elem.addEventListener("click", click); | |
} | |
// Filters | |
statusFilter.addEventListener("change", () => { | |
hideType(statusFilter.value); | |
}); | |
// Begin writing class information | |
for (const [i, course] of Object.entries(courses)) { | |
const template = document.createElement("template"); | |
template.innerHTML = `<div class="canvasall_class_grade_wrapper" canvasall-class-id="${ | |
course.id | |
}"> | |
<div class="canvasall_class_grade_name"> | |
<span>${+i + 1}. <a href="/courses/${course.id}">${ | |
course.name | |
}</a></span> | |
</div> | |
<div class="canvasall_class_grade_score"> | |
<span>(Loading scores...)</span> | |
</div> | |
<div class="canvasall_class_grade_instructor"> | |
<span>(Loading instructors...)</span> | |
</div> | |
</div>`; | |
courseGradeDiv.append(template.content.cloneNode(true)); | |
} | |
log("Getting course assignments and grades"); | |
let courseCount = 0, | |
finishedCollections = 0; | |
for (const course of courses) { | |
courseCount++; | |
getCourseAssignments(course.id, currentUserID).then(assignments => { | |
courseAssignments.push.apply( | |
courseAssignments, | |
assignments | |
); | |
finishedCollections++; | |
if (finishedCollections === courseCount) { | |
// Write assignments | |
courseAssignments.sort((a, b) => { | |
// sort by "due date" | |
// also places non-due-date at end. (+1 week) | |
const dueA = new Date( | |
a.plannable.due_at || b.plannable.created_at || Date.now() + 604800e3 | |
), | |
dueB = new Date( | |
b.plannable.due_at || b.plannable.created_at || Date.now() + 604800e3 | |
); | |
return dueA.getTime() - dueB.getTime(); | |
}); | |
log("Compiling assignments"); | |
for (const assignment of courseAssignments) { | |
const template = document.createElement("template"); | |
if (assignment.plannable_type === "announcement") { | |
// Put in announcement thing | |
template.innerHTML = `<div class="canvasall_announcement_wrapper"> | |
<div class="canvasall_announcement_title"> | |
<a href="${assignment.html_url}">${assignment.plannable.title}</a> | |
</div> | |
<div class="canvasall_announcement_class"> | |
<a href="/courses/${assignment.course_id}">${ | |
assignment.context_name | |
}</a> | |
</div> | |
<div class="canvasall_announcement_when"> | |
<span>${new Date(assignment.plannable.created_at) | |
.toString() | |
.split(" ") | |
.slice(0, 5) | |
.join(" ") | |
.replace(/:\d{2}$/, "") | |
.replace(/\s(?=\w{3}\s\d{2})/, ", ") | |
.replace(/\d{4}/, "at")}</span> | |
</div> | |
</div>`; | |
announcementsDiv.append(template.content.cloneNode(true)); | |
continue; | |
} | |
const divClasses = [], | |
{ submissions } = assignment; | |
if (submissions) { | |
const { excused, graded, has_feedback, late, missing, submitted } = | |
submissions; | |
if (excused || graded) { | |
divClasses.push("canvasall_status_done"); | |
} | |
if (submitted) { | |
divClasses.push("canvasall_status_submit"); | |
} | |
if (late) { | |
divClasses.push("canvasall_status_late"); | |
} | |
if (missing) { | |
divClasses.push("canvasall_status_missing"); | |
} | |
if (has_feedback) { | |
divClasses.push("canvasall_status_feedback"); | |
} | |
} | |
divClasses.push("canvasall_status_" + assignment.plannable_type); | |
template.innerHTML = `<div class="canvasall_assignment_wrapper ${divClasses.join( | |
" " | |
)}" class-id="${assignment.course_id}"> | |
<div class="canvasall_assignment_title"> | |
<div class="canvasall_assignment_icon canvasall_assignment_type_${ | |
assignment.plannable_type | |
}"></div> | |
<a href="${assignment.html_url}">${assignment.plannable.title}</a> | |
</div> | |
<div class="canvasall_assignment_class"> | |
<a href="/courses/${assignment.course_id}">${ | |
assignment.context_name | |
}</a> | |
</div> | |
<div class="canvasall_assignment_due"> | |
<span>${ | |
assignment.plannable.due_at | |
? new Date(assignment.plannable.due_at) | |
.toString() | |
.split(" ") | |
.slice(0, 5) | |
.join(" ") | |
.replace(/:\d{2}$/, "") | |
.replace(/\s(?=\w{3}\s\d{2})/, ", ") | |
.replace(/\d{4}/, "at") | |
: "No due date" | |
}</span> | |
</div> | |
</div>`; | |
assignmentsDiv.append(template.content.cloneNode(true)); | |
} | |
// Restore hide | |
for (const [key, value] of Object.entries(localStorageConfig)) { | |
if (value && key !== "") { | |
hideType(key); | |
} | |
} | |
courseAssignments.splice(0); // Free up memory | |
document.querySelector("#canvasall_fetching_information").outerHTML = ""; | |
} | |
}); | |
// Get grades | |
loadFrame(iFrameLoader, `/courses/${course.id}/grades`).then( | |
(ClassGradeFrame) => { | |
const { document: d, window: w, frame } = ClassGradeFrame; | |
let overallGrade = d | |
.querySelector( | |
"#student-grades-right-content .student_assignment.final_grade > .grade" | |
) | |
?.textContent?.replace(/%|\s/g, ""), | |
titles = d.querySelectorAll(".title"), | |
possiblePoints = d.querySelectorAll(".points_possible"), | |
dueDates = d.querySelectorAll(".due"); | |
courseGrades[course.id] = { | |
Grades: overallGrade + "%", | |
Titles: titles, | |
PossiblePoints: possiblePoints, | |
DueDates: dueDates, | |
}; | |
// Write grades | |
const gradeDiv = courseGradeDiv.querySelector( | |
`[canvasall-class-id="${course.id}"] > .canvasall_class_grade_score` | |
), | |
{ ENV } = w, | |
{ grading_scheme } = ENV, | |
[Letter] = | |
grading_scheme.find((scheme) => { | |
try { | |
return +overallGrade / 100 >= scheme[1]; | |
} catch (e) { | |
console.error(e); | |
} | |
}) || "?"; | |
gradeDiv.innerHTML = | |
overallGrade !== "N/A" | |
? `<span>${Letter} (${overallGrade}%)</span>` | |
: `<span>N/A</span>`; | |
// Done using data from iframe, attempt to clean up memory usage | |
overallGrade = titles = possiblePoints = dueDates = null; | |
for (const i in courseGrades) { | |
delete courseGrades[i]; | |
} | |
w.location = "about:blank"; | |
setTimeout(() => { | |
frame.remove(); | |
}, 500); | |
} | |
); | |
// Get instructors | |
getCourseTeacher(course.id) | |
.then((teacher) => { | |
const teacherDiv = courseGradeDiv.querySelector( | |
`[canvasall-class-id="${course.id}"] > .canvasall_class_grade_instructor` | |
); | |
teacherDiv.innerHTML = `<span>${teacher.name}</span>`; | |
}) | |
.catch(() => { | |
// no teacher, or failed to get teacher | |
const teacherDiv = courseGradeDiv.querySelector( | |
`[canvasall-class-id="${course.id}"] > .canvasall_class_grade_instructor` | |
); | |
teacherDiv.innerHTML = "<span>No instructor found.</span>"; | |
}); | |
} | |
} | |
// /api/v1/users/:user_id/communication_channels | |
/** | |
* getCourses - Gets all the classes of the user | |
* | |
* @returns {Promise<Array>} A list of class information | |
*/ | |
function getCourses() { | |
return new Promise((res, rej) => { | |
const x = new XMLHttpRequest(); | |
x.open( | |
"GET", | |
"/api/v1/users/self/favorites/courses?include[]=term&exclude[]=enrollments&sort=nickname" | |
); | |
x.setRequestHeader("X-Requested-With", "XMLHttpRequest"); | |
x.setRequestHeader( | |
"accept", | |
"application/json, text/javascript, application/json+canvas-string-ids, */*; q=0.01" | |
); | |
x.onload = () => { | |
res(JSON.parse(x.responseText)); | |
}; | |
x.onerror = () => { | |
rej(); | |
}; | |
x.send(); | |
}); | |
} | |
/** | |
* GetClassAssignments - Gets the class assignments | |
* | |
* @param {String} courseID The class id | |
* @param {String} userID The user id | |
* @returns {Promise<Array>} The list of assignments | |
*/ | |
function getCourseAssignments(courseID, userID) { | |
const x = new XMLHttpRequest(), | |
now = new Date(), | |
offset = now.getTimezoneOffset() / 60, | |
nextUrlRegex = /[^<>]*(?=>; rel="next")/; | |
now.addDays(-14); // To get assignments from 2 weeks ago | |
now.setHours(8 - offset); | |
now.setMinutes(0); | |
now.setSeconds(0); | |
now.setMilliseconds(0); | |
function makeHttpRequest(url) { | |
return new Promise((res, rej) => { | |
x.open("GET", url); | |
x.setRequestHeader("X-Requested-With", "XMLHttpRequest"); | |
x.setRequestHeader( | |
"accept", | |
"application/json+canvas-string-ids, application/json+canvas-string-ids, application/json, text/plain, */*" | |
); | |
x.onerror = () => {rej();} | |
x.onload = () => { | |
res(x); | |
} | |
x.send(); | |
}); | |
} | |
async function gatherRecursiveRequests(url, list = []) { | |
const response = await makeHttpRequest(url), | |
data = JSON.parse(response.responseText), | |
linkHeader = response.getResponseHeader("link"); | |
list.push(...data); | |
if (linkHeader) { | |
const lastLink = linkHeader.match(nextUrlRegex); | |
if (lastLink) { | |
return gatherRecursiveRequests(lastLink[0], list); | |
} else { | |
return list; | |
} | |
} else { | |
return list; | |
} | |
} | |
return gatherRecursiveRequests(`/api/v1/planner/items?start_date=${now.toISOString()}&order=asc&context_codes[]=course_${courseID}`) //&context_codes[]=user_${userID} | |
.catch(() => []); | |
} | |
/** | |
* getCourseTeacher - Gets the teacher of the course | |
* | |
* @param {String} courseID The class id | |
* @returns {Object} The teacher | |
*/ | |
function getCourseTeacher(courseID) { | |
return new Promise((res, rej) => { | |
const x = new XMLHttpRequest(); | |
x.open( | |
"GET", | |
`/api/v1/search/recipients?search=&per_page=20&permissions[]=send_messages_all&messageable_only=true&synthetic_contexts=true&context=course_${courseID}_teachers` | |
); | |
x.setRequestHeader("X-Requested-With", "XMLHttpRequest"); | |
x.setRequestHeader( | |
"Accept", | |
"application/json, text/javascript, application/json+canvas-string-ids, */*; q=0.01" | |
); | |
x.onload = () => { | |
res(JSON.parse(x.responseText)[0]); | |
}; | |
x.onerror = () => { | |
rej(); | |
}; | |
x.send(); | |
}); | |
} | |
/** | |
* loadFrame - Loads an iframe | |
* | |
* @param {HTMLElement} iFrameLoader The element to append iframes to | |
* @param {String} src The iframe url to load | |
* @returns {Promise<Object>} returns an object with the "window" and "document" of the iframe | |
*/ | |
function loadFrame(iFrameLoader, src) { | |
return new Promise((res) => { | |
const mainFrame = document.createElement("iframe"); | |
mainFrame.src = src || "/"; | |
iFrameLoader.append(mainFrame); | |
mainFrame.addEventListener("load", () => { | |
const frameContext = { | |
document: mainFrame.contentDocument, | |
window: mainFrame.contentWindow, | |
frame: mainFrame | |
}; | |
res(frameContext); | |
}); | |
}); | |
} | |
load(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment