Skip to content

Commit 90d1df6

Browse files
Stop coding like a child
* Use a `MutationObserver` to actually watch the page, instead of using a timeout * Use spaces instead of tabs * Slightly altered file layout
1 parent 6b92de5 commit 90d1df6

File tree

8 files changed

+241
-247
lines changed

8 files changed

+241
-247
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
firefox/web-ext-artifacts
2+
firefox/.amo-upload-uuid
3+
environ

attendance-calci.js

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
console.log("I am run");
2+
3+
class Attendance {
4+
/* One instance of `Object Attendance` for each subject. Contains
5+
* .tr, .td: the HTML <tr> for that subject in the attendance table, and a <td> in which to dump stuff.
6+
* .attended, .conducted: counts of classes attended and conducted.
7+
* .attn_fn_chooser, .attn_ip_chooser: the <input>s for the parameter to calculate and the input.
8+
* .calc_op: the <> displaying whatever is calculated.
9+
* .chained_Attendance: A new Attendance object, reflecting future attendance after a single operation. (Not declared during construction)
10+
* .chain_btn, .chain_reset_btn: A button to update parameters and accordingly add a new link to the chain, reflecting a future attendance state; and a button to reset the chain
11+
*/
12+
13+
constructor (tr, parent) {
14+
this.tr = tr; // The <tr> associated with the subject. Used as distinction mechanism.
15+
this.td = document.createElement("td");
16+
17+
this.parent = parent || tr;
18+
this.parent.append(this.td);
19+
20+
let classes = this.tr.children[2].innerHTML.split("/");
21+
this.attended = Number(classes[0]);
22+
this.conducted = Number(classes[1]);
23+
24+
this.attn_fn_chooser = document.createElement("select"); // Dropdown to select what to calculate
25+
this.attn_fn_chooser.innerHTML = `
26+
<option value="bunk">bunk __ classes</option>
27+
<option value="target">target __ attendance</option>
28+
<option value="bunk_to_target">bunk, retaining __ attendance</option>
29+
`;
30+
this.attn_fn_chooser.value = "bunk";
31+
this.attn_fn_chooser.onchange = () => { this.eval(); }; // Wrap `eval`, since `this` otherwise takes the value of the target element in `eval`'s context.
32+
this.td.append(this.attn_fn_chooser);
33+
34+
this.attn_ip_chooser = document.createElement("input"); // The input to the calculator
35+
this.attn_ip_chooser.type = "number";
36+
this.attn_ip_chooser.min = 0;
37+
this.attn_ip_chooser.style.display = "inline";
38+
this.attn_ip_chooser.style.width = "5em";
39+
this.attn_ip_chooser.value = 1;
40+
this.attn_ip_chooser.oninput = () => { this.eval("Don't set defaults"); };
41+
this.td.append(this.attn_ip_chooser);
42+
43+
this.calc_op = document.createElement("span");
44+
this.calc_op.style.marginLeft = "1em";
45+
this.td.append(this.calc_op);
46+
47+
this.chained_Attendance = null;
48+
49+
this.chain_btn = document.createElement("button");
50+
this.chain_btn.innerHTML = "Chain"
51+
this.chain_btn.onclick = () => {
52+
this.chain(35, 40); }
53+
this.td.append(this.chain_btn);
54+
55+
this.chain_reset_btn = document.createElement("button");
56+
this.chain_reset_btn.innerHTML = "Reset Chain";
57+
this.chain_reset_btn.onclick = () => {
58+
this.chain_btn.style.display = "initial";
59+
this.chained_Attendance.td.remove();
60+
delete this.chained_Attendance;
61+
};
62+
this.td.append(this.chain_reset_btn);
63+
64+
this.eval();
65+
}
66+
67+
attendance () { // Current attendance as percentage
68+
return this.attended*100/this.conducted; }
69+
70+
target (x) { // How many classes more to reach a target of x% attendance?
71+
if ((x > 99) || x < this.attendance())
72+
return "oops";
73+
x = x/100;
74+
75+
let res = 0;
76+
while ( ((this.attended + res) / (this.conducted + res)) < x)
77+
res += 1;
78+
79+
return res;
80+
}
81+
82+
bunk (x) { // What will be attendance after bunking x classes?
83+
return this.attended*100 / (this.conducted + x);
84+
}
85+
86+
bunk_to_target (x) { // How many classes can I bunk while targetting x% attendance?
87+
if ( (x > this.attendance()) || (x < 1) )
88+
return "oops";
89+
90+
x = x/100;
91+
let res = 0;
92+
93+
while ( ((this.attended) / (this.conducted + res)) > x ) {
94+
res += 1;
95+
}
96+
97+
return res - 1;
98+
}
99+
100+
101+
eval (set_default) { // Run the entire calculation + display routine for given user inputs. Set default values if set_default is not null.
102+
let ip;
103+
let res;
104+
let res_num;
105+
let new_attended;
106+
let new_conducted;
107+
108+
switch (this.attn_fn_chooser.value) {
109+
case "bunk":
110+
if (!set_default)
111+
this.attn_ip_chooser.value = 1;
112+
113+
ip = Number(this.attn_ip_chooser.value);
114+
res = "Will have " + this.bunk(ip).toFixed(2) + "%";
115+
116+
new_attended = this.attended;
117+
new_conducted = this.conducted + ip;
118+
119+
break;
120+
121+
case "target":
122+
if (!set_default) {
123+
this.attn_ip_chooser.min = this.attendance().toFixed(2);
124+
this.attn_ip_chooser.max = 100;
125+
this.attn_ip_chooser.value = Number(this.attn_ip_chooser.min) + 5;
126+
}
127+
128+
ip = Number(this.attn_ip_chooser.value);
129+
res_num = this.target(ip);
130+
res = "Attend " + res_num + " classes";
131+
132+
new_attended = this.attended + res_num;
133+
new_conducted = this.conducted + res_num;
134+
135+
break;
136+
137+
case "bunk_to_target":
138+
if (!set_default) {
139+
this.attn_ip_chooser.max = this.attendance().toFixed(2);
140+
this.attn_ip_chooser.min = 0;
141+
this.attn_ip_chooser.value = Number(this.attn_ip_chooser.max) - 5;
142+
}
143+
144+
ip = Number(this.attn_ip_chooser.value);
145+
res_num = this.bunk_to_target(ip);
146+
res = "Can bunk " + res_num + " classes";
147+
148+
new_attended = this.attended;
149+
new_conducted = this.conducted + res_num;
150+
break;
151+
}
152+
153+
this.chain_btn.onclick = () => {
154+
this.chain(new_attended, new_conducted); };
155+
this.chain_update(new_attended, new_conducted)
156+
this.calc_op.innerHTML = " " + res;
157+
}
158+
159+
chain (attended, conducted) {
160+
this.chained_Attendance = new Attendance(this.tr, this.td);
161+
this.chained_Attendance.attended = attended;
162+
this.chained_Attendance.conducted = conducted;
163+
this.chained_Attendance.eval();
164+
this.chain_btn.style.display = "none";
165+
this.chained_Attendance.chain_reset_btn.style.display = "none";
166+
}
167+
168+
chain_reset () { // Reset the chain -- retain only `this` object.
169+
this.chained_Attendance = null;
170+
}
171+
172+
chain_update (new_attended, new_conducted) { // Propagate changes through the chain
173+
if (!this.chained_Attendance)
174+
return;
175+
176+
this.chained_Attendance.attended = new_attended;
177+
this.chained_Attendance.conducted = new_conducted;
178+
this.chained_Attendance.eval("Don't set defaults");
179+
this.chained_Attendance.chain_update();
180+
181+
}
182+
}
183+
184+
function setup_buttons () {
185+
// Set up buttons for Attendance Calci
186+
// This should be called after the attendance view is loaded.
187+
subs = document.querySelectorAll('#subjetInfo > tr');
188+
189+
for (let i = 0; i < subs.length; i++) {
190+
new Attendance(subs[i]);
191+
}
192+
193+
th = document.createElement("th");
194+
th.innerHTML = "Attendance Calculator";
195+
document.querySelector("#studentAttendanceSemester thead > tr").append(th);
196+
}
197+
198+
observer = new MutationObserver ( (records) => {
199+
// Listen for changes on the content body
200+
for (var rec of records) {
201+
if (rec.target.id == "studentAttendanceSemester") {
202+
setup_buttons ();
203+
break;
204+
}
205+
}
206+
});
207+
observer.observe(
208+
document.querySelector('#StudentProfilePESUContent'),
209+
{'childList': true, 'subtree': true}
210+
);

chrome/attendance-calci.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../attendance-calci.js

chrome/js.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

chrome/manifest.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"manifest_version": 3,
3-
"name": "Pesu Attendance Calci",
4-
"version": "1.0",
2+
"manifest_version": 3,
3+
"name": "Pesu Attendance Calci",
4+
"version": "1.0",
55

6-
"description": "An attendance calculator that integrates with PESU Academy. Written by Vedanth Padmaraman",
7-
"homepage_url": "https://github.com/GlowingScrewdriver/attendance_calci",
6+
"description": "An attendance calculator that integrates with PESU Academy. Written by Vedanth Padmaraman",
7+
"homepage_url": "https://github.com/GlowingScrewdriver/attendance_calci",
88

9-
"host_permissions": ["*://*.pesuacademy.com/*"],
10-
"content_scripts": [{
11-
"matches": ["*://*.pesuacademy.com/*"],
12-
"js": ["js.js"]
13-
}]
9+
"host_permissions": ["*://*.pesuacademy.com/*"],
10+
"content_scripts": [{
11+
"matches": ["*://*.pesuacademy.com/*"],
12+
"js": ["attendance-calci.js"]
13+
}]
1414
}

firefox/attendance-calci.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../attendance-calci.js

0 commit comments

Comments
 (0)