-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathassignments.js
188 lines (158 loc) · 5.68 KB
/
assignments.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
const assignments = [];
function scrapeHref(el) {
return $(el).find("a").attr("href");
}
const sortFns = {
name: (a, b) => a.name.localeCompare(b.name),
submissions: (a, b) => a.submission.localeCompare(b.submission),
score: (a, b) => {
// using localeCompare on text here results in the unscored entries in the wrong place
const aNumerator = a.score.numerator === "-" ? 0 : a.score.numerator;
const bNumerator = b.score.numerator === "-" ? 0 : b.score.numerator;
return bNumerator / b.score.denominator - aNumerator / a.score.denominator;
},
deadline: (a, b) => a.dateObj - b.dateObj,
};
// Scrape assignment names
$(".d_ich").each(function () {
assignments.push({
name: this.textContent.replaceAll('\n', '<br/>'),
descriptionHref: scrapeHref(this),
group: $(this).find(".di_i").length === 1,
turnItIn: $(this).find(".d2l-image").length === 1,
});
});
// Scrape scores
$(".dco.d2l-grades-score > div.dco_c").each(function (index) {
const labels = $(this).children("label");
let scoreObject = {};
if (this.textContent.includes("%")) {
scoreObject.cleanedScore = this.textContent;
} else {
scoreObject.cleanedScore = this.textContent.substring(
0,
this.textContent.length - 3
);
}
scoreObject.numerator = labels[0].textContent;
scoreObject.denominator = labels[2].textContent;
assignments[index].score = scoreObject;
});
// Scrape feedback links
$("#z_b > tbody > tr > td:nth-child(4)").each(function (index) {
assignments[index].feedbackHref = scrapeHref(this);
});
// Scrape submission statuses and deadlines
const submissionsAndDeadlines = [];
$(".d_gn.d_gc.d_gt").each(function () {
submissionsAndDeadlines.push(this);
});
// Populate assignment objects with statuses and deadlines
assignments.forEach((assignment, index) => {
const submission = submissionsAndDeadlines[index * 2];
const deadline = submissionsAndDeadlines[index * 2 + 1];
assignment.submission = submission.textContent;
assignment.submissionHistoryHref = scrapeHref(submission);
assignment.deadline = deadline.textContent;
assignment.dateObj = new Date(deadline.textContent);
});
// Remove the yucky myCourses table
$("#d_content_r_p").remove();
// Initial sort values will be set in initialization down below
let sortType = null;
let reverseSort = false;
function headerClicked(type) {
if (sortType === type) {
reverseSort = !reverseSort;
} else {
sortType = type;
reverseSort = false;
}
assignments.sort(sortFns[type]);
reverseSort && assignments.reverse();
chrome.storage.sync.set({
assignmentSortType: sortType,
assignmentReverseSort: reverseSort,
});
renderTable();
}
function renderTableHeader(name) {
lowerCaseName = name.toLowerCase();
const arrow =
sortType === lowerCaseName
? `<div class="mcp-sort-arrow${reverseSort ? " reverse" : ""}"></div>`
: "";
return `<th id="mcp-${lowerCaseName}-header">${name}${arrow}</th>`;
}
// Returns a table cell with a link if href exists, and a span if not
function renderLink(text, href) {
const contents = href ? `<a href=${href}>${text}</>` : `<span>${text}</span>`;
return `<td>${contents}</td>`;
}
function renderNameColumn(a) {
const contents = a.descriptionHref ? `<a href=${a.descriptionHref}>${a.name}</>` : `<span>${a.name}</span>`;
const groupImage = a.group ? `<img class="mcp-svg-image" src=${chrome.extension.getURL("/images/group.svg")}>` : ``;
const turnItInImage = a.turnItIn ? `<img class="mcp-svg-image" src=${chrome.extension.getURL("/images/turnitin.svg")}>` : ``;
return `<td>${contents}${groupImage}${turnItInImage}</td>>`;
}
function renderTable() {
// Remove existing MCP table
$(".mcp-assignments").remove();
// Create new table
$("#d_content_r_c2").append(`
<table class="mcp-assignments">
<tr>
${renderTableHeader("Name")}
${renderTableHeader("Submissions")}
${renderTableHeader("Score")}
${renderTableHeader("Deadline")}
</tr>
</table>
`);
// Add click handlers to table headers
$("#mcp-name-header").click(() => headerClicked("name"));
$("#mcp-submissions-header").click(() => headerClicked("submissions"));
$("#mcp-score-header").click(() => headerClicked("score"));
$("#mcp-deadline-header").click(() => headerClicked("deadline"));
// Hide submitted assignments, if appropriate
const filteredAssignments = $("#hide-submitted").is(":checked")
? assignments.filter((a) => !a.submission.includes("Submission"))
: assignments;
// Populate table
filteredAssignments.forEach((a) =>
$(".mcp-assignments").append(`
<tr>
${renderNameColumn(a)}
${renderLink(a.submission, a.submissionHistoryHref)}
${renderLink(a.score.cleanedScore, a.feedbackHref)}
<td>${a.deadline}</td>
</tr>
`)
);
}
chrome.storage.sync.get(
["hideSubmitted", "assignmentSortType", "assignmentReverseSort"],
({ hideSubmitted, assignmentSortType, assignmentReverseSort }) => {
// Inject "Hide submitted" toggle
$("#d_content_r > div > ul > li").prepend(`
<input class="toggle" type="checkbox" id="hide-submitted"/>
<label for="hide-submitted">Hide submitted</label>
`);
// When it's clicked, toggle the value in storage and re-render table
$("#hide-submitted")
.prop("checked", hideSubmitted)
.click(() => {
chrome.storage.sync.set({
hideSubmitted: $("#hide-submitted").is(":checked"),
});
renderTable();
});
// Remember previous sort configuration
sortType = assignmentSortType;
reverseSort = assignmentReverseSort;
assignments.sort(sortFns[sortType]);
reverseSort && assignments.reverse();
// Initial table render
renderTable();
}
);