Last active
May 21, 2021 13:23
-
-
Save jrr6/5f7961a3e3a1b582fae94a92ee712250 to your computer and use it in GitHub Desktop.
A userscript that adds a card showing undone work to the Google Classroom Classes page.
This file contains hidden or 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 Classroom Work Card | |
| // @version 0.1.8 | |
| // @description Adds a card showing undone work to the Google Classroom Classes page. | |
| // @author jrr6 | |
| // @downloadURL https://gist.github.com/jrr6/5f7961a3e3a1b582fae94a92ee712250/raw/ClassroomWorkCard.user.js | |
| // @updateURL https://gist.github.com/jrr6/5f7961a3e3a1b582fae94a92ee712250/raw/ClassroomWorkCard.user.js | |
| // @match https://classroom.google.com/* | |
| // @grant GM_addStyle | |
| // ==/UserScript== | |
| function addCard () { | |
| var css = document.createElement('style') | |
| css.type = 'text/css' | |
| var styles = ` | |
| #work-spot { | |
| width: 27.5rem; | |
| height: 20.5em; | |
| overflow: scroll; | |
| margin-bottom: 2rem; | |
| text-align: center; | |
| margin-right: 2.5rem; | |
| box-shadow: 0 0.1rem 0.2rem rgba(0,0,0,0.12), 0 0 0.1rem rgba(0,0,0,0.12); | |
| padding: 1em; | |
| background: #fff; | |
| } | |
| #work-spot:hover { | |
| box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); | |
| } | |
| #work-spot h1 { | |
| font-size: 2rem; | |
| font-weight: 500; | |
| line-height: 2.8rem; | |
| } | |
| .work-item h3 { | |
| font-weight: bold; | |
| } | |
| #work-spot h2 { | |
| color: gray; | |
| font-weight: bold; | |
| } | |
| .work-item { | |
| border: 1px solid black; | |
| margin-bottom: 1em; | |
| padding: 0.5em; | |
| } | |
| ` | |
| if (css.styleSheet) { | |
| css.styleSheet.cssText = styles | |
| } else { | |
| css.appendChild(document.createTextNode(styles)) | |
| } | |
| document.getElementsByTagName('head')[0].appendChild(css) | |
| var workSpot = document.createElement('div') | |
| workSpot.setAttribute('id', 'work-spot') | |
| var h1 = document.createElement('h1') | |
| h1.textContent = 'Undone Work' | |
| workSpot.appendChild(h1) | |
| document.querySelector('.JwPp0e').prepend(workSpot) | |
| // script | |
| var dates = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] | |
| var todayIdx = (new Date()).getDay() | |
| dates = dates.splice(todayIdx).concat(dates) | |
| var asgns = [] | |
| document.querySelectorAll('div.kdAl3b div[jscontroller="x4VfFc"]').forEach(e => { | |
| let assignmentGroups = Array.from(e.children).filter(e => e.classList.contains('hrUpcomingAssignmentGroup')) | |
| assignmentGroups.forEach(e => { | |
| let dueDate = e.previousSibling.textContent.slice(4) | |
| let className = e.parentElement.parentElement.parentElement.children[0].children[2].children[0].children[0].children[0].children[0].textContent | |
| for (let child of e.children) { | |
| let asgnName = child | |
| asgns.push({date: dueDate, classN: className, nameNode: asgnName}) | |
| } | |
| }) | |
| }) | |
| asgns.sort((a, b) => { | |
| let aIdx = dates.indexOf(a.date) | |
| let bIdx = dates.indexOf(b.date) | |
| // special cases | |
| if (a.date === 'today' && b.date === 'today') { | |
| return 0 | |
| } else if (a.date === 'today') { | |
| return -1 | |
| } else if (b.date === 'today') { | |
| return 1 | |
| } | |
| if (a.date === 'tomorrow' && b.date === 'tomorrow') { | |
| return 0 | |
| } else if (a.date === 'tomorrow') { | |
| return -1 | |
| } else if (b.date === 'tomorrow') { | |
| return 1 | |
| } | |
| if (aIdx === -1 && bIdx === -1) { | |
| return 0 | |
| } else if (aIdx === -1) { | |
| return 1 | |
| } else if (bIdx === -1) { | |
| return -1 | |
| } | |
| return aIdx > bIdx ? 1 : aIdx === bIdx ? 0 : -1 | |
| }) | |
| var wList = document.getElementById('work-spot') | |
| if (asgns.length === 0) { | |
| wList.textContent = 'No more work!' | |
| } else { | |
| for (let i = 0; i < asgns.length; ++i) { | |
| let box = document.createElement('div') | |
| box.classList.add('work-item') | |
| if (i > 0) { | |
| if (asgns[i - 1].date !== asgns[i].date) { | |
| let date = document.createElement('h2') | |
| date.textContent = `Due ${asgns[i].date}` | |
| wList.appendChild(date) | |
| } | |
| } else { | |
| let date = document.createElement('h2') | |
| date.textContent = `Due ${asgns[i].date}` | |
| wList.appendChild(date) | |
| } | |
| let classN = document.createElement('h3') | |
| classN.textContent = asgns[i].classN | |
| box.appendChild(classN) | |
| box.appendChild(asgns[i].nameNode.cloneNode(true)) | |
| wList.appendChild(box) | |
| } | |
| } | |
| } | |
| function rafAsync () { | |
| return new Promise(resolve => { | |
| requestAnimationFrame(resolve) | |
| }) | |
| } | |
| function checkElements (selector) { | |
| if (document.querySelector(selector) === null) { | |
| return rafAsync().then(() => checkElements(selector)) | |
| } else { | |
| return Promise.resolve(true) | |
| } | |
| } | |
| var mo = new MutationObserver(function () { | |
| if (document.querySelector('.JwPp0e') !== null) { | |
| checkElements('.gHz6xd.yckQJf.lziZub.k6fwTc').then(setTimeout(addCard, 3000)) | |
| mo.disconnect() | |
| } | |
| }) | |
| mo.observe(document.body, {attributes: false, childList: true, subtree: true}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment