added bookmarklet distro to github

This commit is contained in:
EthanThatOneKid 2021-01-02 14:30:47 -08:00
parent 4adcbd1c36
commit 763e2a601f
4 changed files with 134 additions and 9 deletions

View file

@ -1,7 +1,14 @@
# `sensei-grading-bookmarklet`
[![Bookmarklet Size][bookmarklet_size_badge]][bookmarklet_file]
> A bookmarklet to enhance the grading experience of code ninja assignments.
## Purpose
Grading code ninjas' submissions is slow and tedious because the current site has a poor user experience.
My goal is to make an entirely keyboard-navigable enhancement in the form of a bookmarklet.
## Usage
1. Visit the [grading console][grading_console] while logged in as a code sensei.
@ -10,6 +17,16 @@
1. Click <kbd>enter</kbd> to execute the bookmarklet code.
1. Grade.
## Keybinds
| Key | Action |
| ---------------- | ------------------------------ |
| <kbd>i</kbd> | Marks assignment as incomplete |
| <kbd>1</kbd> | Rates assignment as 1-star |
| <kbd>2</kbd> | Rates assignment as 2-star |
| <kbd>3</kbd> | Rates assignment as 3-star |
| <kbd>enter</kbd> | Submits assigment grade |
## Development
This [bookmarklet][bookmarklet_definition] is compiled using [NodeJS][node_installation].
@ -18,11 +35,6 @@ Upon cloning this project, execute `npm i` to install of the necessary dependenc
To run in development mode, run `npm run dev`.
To generate the bookmarklet, run `npm start`.
## Purpose
Grading code ninjas' submissions is slow and tedious because the current site has a poor user experience.
My goal is to make an entirely keyboard-navigable enhancement in the form of a bookmarklet.
## Security
For security reasons, modern browsers will make you type out the prefix (`javascript:`) manually.
@ -32,3 +44,5 @@ This code, however, is non-malicious, so no worries in this case.
[grading_console]: https://gdp.code.ninja/Grading
[bookmarklet_definition]: https://en.wikipedia.org/wiki/Bookmarklet
[node_installation]: https://nodejs.org/en/download/
[bookmarklet_size_badge]: https://img.badgesize.io/EthanThatOneKid/code-sensei/main/sensei-grading-bookmarklet/bookmarklet.txt
[bookmarklet_file]: bookmarklet.txt

View file

@ -1,3 +1,112 @@
// https://gist.github.com/EthanThatOneKid/95e8393c0afc1a35efda6f514d4ee6ea
alert("Hello, World!");
const selectors = {
incompleteBtn: "#IsIncompleteYes",
notesTextarea:
"#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(8) > td:nth-child(2) > textarea",
star1:
"#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(1)",
star2:
"#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(2)",
star3:
"#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(3)",
submit: "#grade-form > button",
previewContainer: "#gradingNode > div > div:nth-child(2)",
assignmentBtn:
"body > div.body-content > div:nth-child(2) > div:nth-child(3) > table > tbody > tr > td:nth-child(7) > a",
assignmentLink: "#gradingNode > div > div:nth-child(2) > div > a",
};
let currentAssignmentOnPage = 0;
const defaultIdleTimeout = 5e3,
assignmentLoadDelay = 1e3;
const setIdleTimeout = (...args) => {
let timeoutId = setTimeout(...args);
const alertUserAction = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(...args);
};
return alertUserAction;
};
const actions = {
incomplete() {
// Sensei declares game incomplete.
document.querySelector(selectors.incompleteBtn).click();
const textarea = document.querySelector(selectors.notesTextarea);
textarea.focus();
const updateTextarea = setIdleTimeout(
() => document.querySelector(selectors.submit).focus(),
defaultIdleTimeout
);
textarea.addEventListener("keypress", updateTextarea);
},
rate(stars) {
// Sensei gives a rating out of 3 stars.
const selectorKey = `star${stars}`;
if (selectorKey in selectors) {
document.querySelector(selectors[selectorKey]).click();
this.submit();
}
},
submit() {
document.querySelector(selectors.submit).click();
currentAssignmentOnPage++;
this.nextAssignment();
},
nextAssignment() {
document
.querySelectorAll(selectors.assignmentBtn)
[currentAssignmentOnPage].click();
setTimeout(() => {
const preview = document.createElement("iframe");
preview.width = "1600px";
preview.height = "1400px";
preview.src = document.querySelector(selectors.assignmentLink).href;
document.querySelector(selectors.previewContainer).innerHTML = "";
document.querySelector(selectors.previewContainer).appendChild(preview);
document
.querySelector(selectors.previewContainer)
.parentElement.scrollIntoView();
}, assignmentLoadDelay);
},
};
document.addEventListener("keypress", (event) => {
try {
switch (event.key) {
case "i":
actions.incomplete();
break;
case "1":
actions.rate(1);
break;
case "2":
actions.rate(2);
break;
case "3":
actions.rate(3);
break;
case "enter":
actions.submit();
break;
default:
break;
}
} catch (error) {
console.log(`Sensei Error: ${error}`);
}
});
const main = () => {
actions.nextAssignment();
};
main();

View file

@ -0,0 +1 @@
javascript:(function()%7B%22use%20strict%22%3Bvar%20selectors%3D%7BincompleteBtn%3A%22%23IsIncompleteYes%22%2CnotesTextarea%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(8)%20%3E%20td%3Anth-child(2)%20%3E%20textarea%22%2Cstar1%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(1)%22%2Cstar2%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(2)%22%2Cstar3%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(3)%22%2Csubmit%3A%22%23grade-form%20%3E%20button%22%2CpreviewContainer%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(2)%22%2CassignmentBtn%3A%22body%20%3E%20div.body-content%20%3E%20div%3Anth-child(2)%20%3E%20div%3Anth-child(3)%20%3E%20table%20%3E%20tbody%20%3E%20tr%20%3E%20td%3Anth-child(7)%20%3E%20a%22%2CassignmentLink%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(2)%20%3E%20div%20%3E%20a%22%7D%2CcurrentAssignmentOnPage%3D0%2CdefaultIdleTimeout%3D5e3%2CassignmentLoadDelay%3D1e3%2CsetIdleTimeout%3Dfunction()%7Bfor(var%20e%3Darguments.length%2Ct%3DArray(e)%2Cn%3D0%3Bn%3Ce%3Bn%2B%2B)t%5Bn%5D%3Darguments%5Bn%5D%3Bvar%20i%3DsetTimeout.apply(void%200%2Ct)%3Breturn%20function()%7BclearTimeout(i)%2Ci%3DsetTimeout.apply(void%200%2Ct)%7D%7D%2Cactions%3D%7Bincomplete%3Afunction()%7Bdocument.querySelector(selectors.incompleteBtn).click()%3Bvar%20e%3Ddocument.querySelector(selectors.notesTextarea)%3Be.focus()%3Bvar%20t%3DsetIdleTimeout(function()%7Breturn%20document.querySelector(selectors.submit).focus()%7D%2CdefaultIdleTimeout)%3Be.addEventListener(%22keypress%22%2Ct)%7D%2Crate%3Afunction(e)%7Be%3D%22star%22%2Be%3Be%20in%20selectors%26%26(document.querySelector(selectors%5Be%5D).click()%2Cthis.submit())%7D%2Csubmit%3Afunction()%7Bdocument.querySelector(selectors.submit).click()%2CcurrentAssignmentOnPage%2B%2B%2Cthis.nextAssignment()%7D%2CnextAssignment%3Afunction()%7Bdocument.querySelectorAll(selectors.assignmentBtn)%5BcurrentAssignmentOnPage%5D.click()%2CsetTimeout(function()%7Bvar%20e%3Ddocument.createElement(%22iframe%22)%3Be.width%3D%221600px%22%2Ce.height%3D%221400px%22%2Ce.src%3Ddocument.querySelector(selectors.assignmentLink).href%2Cdocument.querySelector(selectors.previewContainer).innerHTML%3D%22%22%2Cdocument.querySelector(selectors.previewContainer).appendChild(e)%2Cdocument.querySelector(selectors.previewContainer).parentElement.scrollIntoView()%7D%2CassignmentLoadDelay)%7D%7D%3Bdocument.addEventListener(%22keypress%22%2Cfunction(e)%7Btry%7Bswitch(e.key)%7Bcase%22i%22%3Aactions.incomplete()%3Bbreak%3Bcase%221%22%3Aactions.rate(1)%3Bbreak%3Bcase%222%22%3Aactions.rate(2)%3Bbreak%3Bcase%223%22%3Aactions.rate(3)%3Bbreak%3Bcase%22enter%22%3Aactions.submit()%7D%7Dcatch(e)%7Bconsole.log(%22Sensei%20Error%3A%20%22%2Be)%7D%7D)%3Bvar%20main%3Dfunction()%7Bactions.nextAssignment()%7D%3Bmain()%3B%7D)()

View file

@ -1,6 +1,5 @@
// https://github.com/mrcoles/bookmarklet
const fs = require("fs");
const bookmarklet = require("bookmarklet");
const bookmarklet = require("bookmarklet"); // https://github.com/mrcoles/bookmarklet
fs.readFile("./app.js", "utf8", (error, content) => {
if (error !== null) {
@ -8,5 +7,7 @@ fs.readFile("./app.js", "utf8", (error, content) => {
}
const { code, options } = bookmarklet.parseFile(content);
const dist = bookmarklet.convert(code, options);
console.log(`Here is your bookmarklet:\n${dist}`);
fs.writeFile("bookmarklet.txt", dist, undefined, () => {
console.log(`Here is your bookmarklet:\n${dist}`);
});
});