// ==UserScript==
// @name Brewer's Web Enhancements
// @namespace http://tampermonkey.net/
// @version 2025-02-10
// @description Adds functionality to Millennium, Google Classroom and Edval
// @author Alex Brewer
// @match https://classroom.google.com/*
// @match https://millennium.education/*
// @match https://rousehillhs.edval.education/*
// @icon https://millennium.education/favicon.ico
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// ==/UserScript==
// UPDATES:
// Fixed regex to match class name for truancy email links - no longer breaks for (eg.) PMAA.A
(function() {
//TODOS -> Edval block rooming, Google classroom - highlighting, settings modal
// make a GC load prefs and collect prefs function? that works with a GC specific settings modal
const userPrefKey = "BWE20241125";
const pastUserPrefKeys = ["BWE20241120", "RHHS20241121", "RHHS20241120"];
const highlightClasses = true; // true or false
const hideClassImages = true; // true or false
const linksDashboardLink = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSE8KQ5jFbMrgjLZL1Nr1Vyi9kTJxIXzTDA-yf1KO_vC7QAmhS1LaGXINN6NU0Bd5pOVscSaL1RnPPe/pubhtml?gid=0&single=true"
var userData = {
names: [{name:''}],
namesTwo: [{name:''}],
millenniumToggles:
{
header: true,
truancy: true,
timetable: true,
photoRoll: true
},
customLink:{
name: "🔗 LINKS",
url: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSE8KQ5jFbMrgjLZL1Nr1Vyi9kTJxIXzTDA-yf1KO_vC7QAmhS1LaGXINN6NU0Bd5pOVscSaL1RnPPe/pubhtml?gid=0&single=true"
},
gClassroomToggles:
{
condenseCards: true,
hideClasses: false,
highlightClasses: false,
hideImages: true
},
gClassroomCodes: []
};
var currentURL = window.location.href;
console.log(`Running script - Brewer's Web Enhancements`);
var existingUserPrefs = GM_getValue(userPrefKey, null);
if(existingUserPrefs){
var existingUserPrefObj = JSON.parse(existingUserPrefs);
userData = existingUserPrefObj;
console.log("Loaded preferences: ", userData);
}
else{
console.log("Checking for older versions of user preference data");
var prefsFound = false;
for(var ver of pastUserPrefKeys){
var verPrefs = GM_getValue(ver, null);
if(verPrefs){
userData = JSON.parse(verPrefs);
console.log(`Loaded preference from version ${ver}`);
console.log(userData);
savePrefs(userData);
prefsFound = true;
break;
}
}
if(!prefsFound){
console.log("No preferences found - using defaults");
}
}
const r_timetable = /timetable6\.asp/g;
const r_rollmarking = /attendance3\.asp/g;
const r_millennium = /millennium.education\/./g;
const r_edval = /edval/g;
const r_gClassroom = /classroom\.google\.com((\/h)|(\/))*(\s|$)/g;
const popupHTMLcode = `
<div class="popupcontainer">
<form id="millenniumSettingsForm" onsubmit="return false;">
<h2>Millennium Enhancements - Script Settings</h2>
<div class="popuprow"><div>Change site header</div><div>
<input type="radio" id="siteHeaderOn" name="siteHeader" value="true" checked />
<label for="siteHeaderOn">Enable</label>
<input type="radio" id="siteHeaderOff" name="siteHeader" value="false"/>
<label for="siteHeaderOff">Disable</label>
</div></div>
<div class="popuprow"><div>Customise link in header</div>
</div>
<div class="popuprow"><div style="width:40%;"></div><label for="customLinkName">Link Title: </label><input type="text" id="customLinkName", id="customLinkName">
</div>
<div class="popuprow"><div style="width:40%;"></div><label for="customLinkURL">Link URL: </label><input type="text" id="customLinkURL", id="customLinkURL">
</div>
<div class="popuprow"><div>Add one-click truancy email links to rolls</div><div>
<input type="radio" id="truancyLinksOn" name="truancyLinks" value="true" checked />
<label for="truancyLinksOn">Enable</label>
<input type="radio" id="truancyLinksOff" name="truancyLinks" value="false"/>
<label for="truancyLinksOff">Disable</label>
</div></div>
<div class="popuprow"><div>Enable photo roll-marking checkbox</div><div>
<input type="radio" id="photoRollsOn" name="photoRolls" value="true" checked />
<label for="photoRollsOn">Enable</label>
<input type="radio" id="photoRollsOff" name="photoRolls" value="false"/>
<label for="photoRollsOff">Disable</label>
</div></div>
<div class="popuprow"><i> * Click "use photo roll" on roll-marking screen</i></div>
<div class="popuprow"><div>Enable master timetable custom view</div><div>
<input type="radio" id="masterTimetableOn" name="masterTimetable" value="true" checked />
<label for="masterTimetableOn">Enable</label>
<input type="radio" id="masterTimetableOff" name="masterTimetable" value="false"/>
<label for="masterTimetableOff">Disable</label>
</div></div>
<div class="popuprow"></div>
<h3>Master timetable - View settings</h3>
<div class="popuprow"><p>Enter the names of staff to display on the master timetable view.<br>Separate each name with a comma ( , ).
<br>
<br> *You can use a partial name match
<br> *Names are case sensitive
<br> *If either group is left blank, *all* staff will be displayed for that group.</p></div>
<div class="popuprow"></div>
<div class="popuprow">
<div>Timetable group 1:</div>
<textarea id="namesInput" name="names" rows="5" cols="30" value=""></textarea>
</div>
<div class="popuprow">
<div>Timetable group 2:</div>
<textarea id="namesInputTwo" name="namesTwo" rows="5" cols="30" value=""></textarea>
</div>
<h2>Google Classroom - Script settings</h2>
<div class="popuprow"><div>Compact dashboard view</div><div>
<input type="radio" id="condenseCardsOn" name="condenseCards" value="true" checked />
<label for="condenseCardsOn">Enable</label>
<input type="radio" id="condenseCardsOff" name="condenseCards" value="false"/>
<label for="condenseCardsOff">Disable</label>
</div></div>
<div class="popuprow"><div>Hide class images on dashboard</div><div>
<input type="radio" id="hideImagesOn" name="hideImages" value="true" checked />
<label for="hideImagesOn">Enable</label>
<input type="radio" id="hideImagesOff" name="hideImages" value="false"/>
<label for="hideImagesOff">Disable</label>
</div></div>
<div class="popuprow">
<div>
My Classes (url codes, comma separated) <br>
<i>*Use the highlighted part of each class url:<br>
eg. classroom.google.com/c/<mark>Nz2ZstCzNzk3</mark></i><br>
</div>
<div style="width:16px;"></div>
<textarea id="classCodesInput" name="classCodesInput" rows="5" cols="30" value=""></textarea>
</div>
<div class="popuprow"><div>Hide other classes in sidebar</div><div>
<input type="radio" id="hideClassesOn" name="hideClasses" value="true" checked />
<label for="hideClassesOn">Enable</label>
<input type="radio" id="hideClassesOff" name="hideClasses" value="false"/>
<label for="hideClassesOff">Disable</label>
</div></div>
<div class="popuprow"><div>Highlight my classes on dashboard</div><div>
<input type="radio" id="highlightClassesOn" name="highlightClasses" value="true" checked />
<label for="highlightClassesOn">Enable</label>
<input type="radio" id="highlightClassesOff" name="highlightClasses" value="false"/>
<label for="highlightClassesOff">Disable</label>
</div></div>
<div class="popuprow"><div></div><div id="savedIndicator">Saved! -> Reload page to see changes</div></div>
<div class="popuprow"><button id="closeSettings">CLOSE</button><input type="submit" value="submit" id="saveSettings"></input></div>
</form>
</div>
`;
const buttonHTMLcode = '<strong>Settings</strong>'
function truancyEmailLinks() {
const tablerows=document.getElementById("container").children[1].children[0].children[6].children[3].children[0].children
//Get the text of the class selection field
const classSelected = document.getElementsByClassName("chosen-single")[1].innerText
const rclassName = /(\d+\w+\..)|(\w+\.\w\d)|(\w+\.\w)/;
const rclassPeriod = /\d(?=:)/;
const rspace = /\s/g;
const rlparen = /\(/g;
const rrparen = /\)/g;
const className = classSelected.match(rclassName)[0];
const classPeriod = classSelected.match(rclassPeriod)[0];
const rnames = /([A-z]+-([A-z])+)|([A-Z][a-z]+)/g;
function getTime(){
var date = new Date;
var currentTime = date.getHours()*100 + date.getMinutes();
var currentHH = Math.floor(currentTime/100)%12;
var AmPm;
Math.floor(currentTime/100)>12 ? AmPm = 'pm' : AmPm = 'am';
var currentMM = currentTime%100;
if(currentHH==0){currentHH=12;}
if(currentMM.toString().length==1){currentMM = `0${currentMM}`}
var currentHHMM = currentHH+':'+currentMM;
return `${currentHH}:${currentMM}${AmPm}`;
}
const address = '[email protected]';
for (const row of tablerows) {
var name=row.children[0].innerText; //Gets string from name fields
if(name.includes(",")) //Limit to strings that are actually names
{
var names = name.match(rnames);
var surname = names[0];
var firstname=names[1];
if(name.includes("["))
{
firstname = names[2];
}
const subject = `${firstname} ${surname} - ${className} (P${classPeriod})`.replaceAll(rspace,'%20').replaceAll(rlparen,`%28`).replaceAll(rrparen,`%29`);
var body = `${firstname} has walked out of class at ${getTime()}`.replaceAll(rspace,'%20');
var link = document.createElement("a");
link.innerText = "[T]";
link.style.margin = "0px 2px";
link.href = `mailto:${address}?subject=${subject}&body=${body}`;
link.title = "Truant - walked out";
body = `${firstname} has been marked present at school today, but has not yet arrived to Maths.`.replaceAll(rspace,'%20');
var linktwo = document.createElement("a");
linktwo.innerText = "[A]";
linktwo.style.margin = "0px 3px";
linktwo.href = `mailto:${address}?subject=${subject}&body=${body}`;
linktwo.title = "Absent, but marked present at school";
//Add button to create Millennium incident next to each student
var studentUid = row.children[0].children[0].getAttribute("uid");
var incidentBtn = document.createElement("a");
incidentBtn.classList.add("add");
incidentBtn.title="Add incident";
incidentBtn.innerText="add";
incidentBtn.href=`https://millennium.education/admin/register/incident.asp?uid=${studentUid}`
row.children[0].insertAdjacentElement("afterbegin", linktwo);
row.children[0].insertAdjacentElement("afterbegin", link);
row.children[0].insertAdjacentElement("afterbegin", incidentBtn);
}
}
} //End of Truancy email links script
function savePrefs(prefs){
var prefString = JSON.stringify(prefs);
GM_setValue(userPrefKey, prefString);
}
function addMillenniumSettings(){
var butt = document.createElement('button');
var popup = document.createElement('dialog');
popup.classList.add('popup');
butt.classList.add('butt');
butt.innerHTML=buttonHTMLcode;
popup.innerHTML = popupHTMLcode;
document.body.appendChild(butt);
document.body.appendChild(popup);
var currentUserPrefs = GM_getValue(userPrefKey, null);
function collectPrefs(){
var namesInputString = document.getElementById("namesInput").value;
var namesInputArray = namesInputString.split(',');
var trimmedArray = namesInputArray.map((x)=>{return x.trim();});
var namesObj = []
for(var item of trimmedArray){
namesObj.push({"name":item});
}
userData.names = namesObj;
var namesListTwo = document.getElementById("namesInputTwo").value.split(',');
var trimmedListTwo = namesListTwo.map((x)=>{return x.trim();});
var namesObjTwo = []
for(var itemTwo of trimmedListTwo){
namesObjTwo.push({"name":itemTwo});
}
userData.namesTwo = namesObjTwo;
var gClassList = document.getElementById("classCodesInput").value.split(',');
var trimmedgClassList = gClassList.map((x)=>{return x.trim();});
var gClassListObj = []
for(var gClassListItem of trimmedgClassList){
gClassListObj.push(gClassListItem);
}
userData.gClassroomCodes = gClassListObj;
userData.customLink.name = document.getElementById("customLinkName").value;
userData.customLink.url = document.getElementById("customLinkURL").value;
userData.millenniumToggles.header = document.getElementById("siteHeaderOn").checked;
userData.millenniumToggles.truancy = document.getElementById("truancyLinksOn").checked;
userData.millenniumToggles.photoRoll = document.getElementById("photoRollsOn").checked;
userData.millenniumToggles.timetable = document.getElementById("masterTimetableOn").checked;
userData.gClassroomToggles.condenseCards = document.getElementById("condenseCardsOn").checked;
userData.gClassroomToggles.hideImages = document.getElementById("hideImagesOn").checked;
userData.gClassroomToggles.hideClasses = document.getElementById("hideClassesOn").checked;
userData.gClassroomToggles.highlightClasses = document.getElementById("highlightClassesOn").checked;
savePrefs(userData);
document.getElementById("savedIndicator").style.display="block";
}
function loadPrefs(){
document.getElementById("savedIndicator").style.display="none";
if(!currentUserPrefs){
var userName = document.getElementsByClassName('school')[0].innerText.split(":")[1].split(" ");
var userSurname = userName[userName.length-1];
userData.names.push({name:userSurname});
savePrefs(userData);
currentUserPrefs = userData;
loadPrefs();
collectPrefs();
window.location.reload();
}
else{
currentUserPrefs = GM_getValue(userPrefKey, null);
var userPrefObj = JSON.parse(currentUserPrefs);
userData = userPrefObj;
}
if(userData.names){
var nameString = '';
for(var item of userData.names){
if(nameString.length==0){
nameString = nameString.concat('', item.name);
}
else{
nameString = nameString.concat(', ', item.name);
}
}
document.getElementById("namesInput").value=nameString;
}
if(userData.namesTwo){
var nameStringTwo = '';
for(var itemTwo of userData.namesTwo){
if(nameStringTwo.length==0){
nameStringTwo = nameStringTwo.concat('', itemTwo.name);
}
else{
nameStringTwo = nameStringTwo.concat(', ', itemTwo.name);
}
}
document.getElementById("namesInputTwo").value=nameStringTwo;
}
if(userData.gClassroomCodes){
var classCodesString = '';
for(var classCode of userData.gClassroomCodes){
if(classCodesString.length==0){
classCodesString = classCodesString.concat('', classCode);
}
else{
classCodesString = classCodesString.concat(', ', classCode);
}
}
document.getElementById("classCodesInput").value=classCodesString;
}
document.getElementById("customLinkName").value = userData.customLink.name;
document.getElementById("customLinkURL").value = userData.customLink.url ;
if(userData.millenniumToggles.header){
document.getElementById("siteHeaderOn").checked = true;
}
else{
document.getElementById("siteHeaderOff").checked = true;
}
if(userData.millenniumToggles.truancy){
document.getElementById("truancyLinksOn").checked = true;
}
else{
document.getElementById("truancyLinksOff").checked = true;
}
if(userData.millenniumToggles.photoRoll){
document.getElementById("photoRollsOn").checked = true;
}
else{
document.getElementById("photoRollsOff").checked = true;
}
if(userData.millenniumToggles.timetable){
document.getElementById("masterTimetableOn").checked = true;
}
else{
document.getElementById("masterTimetableOff").checked = true;
}
if(userData.gClassroomToggles.condenseCards){
document.getElementById("condenseCardsOn").checked = true;
}
else{
document.getElementById("condenseCardsOff").checked = true;
}
if(userData.gClassroomToggles.hideClasses){
document.getElementById("hideClassesOn").checked = true;
}
else{
document.getElementById("hideClassesOff").checked = true;
}
if(userData.gClassroomToggles.highlightClasses){
document.getElementById("highlightClassesOn").checked = true;
}
else{
document.getElementById("highlightClassesOff").checked = true;
}
}
document.getElementById("closeSettings").addEventListener("click", ()=>{popup.close();});
document.getElementById("millenniumSettingsForm").addEventListener("submit", ()=>{collectPrefs();});
butt.addEventListener("click", ()=>{loadPrefs();popup.showModal();});
loadPrefs();
}
function enhanceSiteHeader(){
const siteHeader = document.getElementById("siteheader").children[0];
// Deletes the redundant links on Millennium homepage
if(userData.millenniumToggles.header){
if(document.getElementById("icons")){document.getElementById("icons").remove();}
}
var links = [
["📢 NOTICES","https://millennium.education/admin/resources/notices.asp"],
["📆 CALENDAR", "https://millennium.education/admin/resources/calendar.asp"],
["📋 CLASSES", "https://millennium.education/admin/users/classes.asp"],
["✍️ ROLLS", "https://millennium.education/admin/schedule/attendance6.asp"],
["⏰ TIMETABLE","https://millennium.education/admin/schedule/timetable6.asp"],
[userData.customLink.name, userData.customLink.url],
// ["🔗 LINKS",linksDashboardLink],
["🏠 HOME","https://millennium.education/admin/home/"],
];
var modifiedHeader = document.createElement("div");
for (var link of links){
var newLink = document.createElement("a");
newLink.href = link[1];
newLink.innerText = link[0];
newLink.classList.add("header-links");
modifiedHeader.insertAdjacentElement("afterBegin", newLink);
}
const headerLinkStyle = `
.header-links{
color: #D75;
flex-grow: 1;
text-align: center;
}`;
var headerCss = document.createElement("style");
headerCss.innerHTML = headerLinkStyle;
document.body.append(headerCss);
if(userData.millenniumToggles.header){
//Use querySelector to delete Millenium banner as name changes on each page
var banners = document.querySelectorAll( '[ class^="banner_" ]');
for(var ban of banners){ban.remove();}
document.getElementById("plane").remove();
//confusingly, I've named the new header 'siteheader', so use getElementById to access the original 'siteheader'
document.getElementById("siteheader").style.margin = "0px 45px";
document.getElementById("siteheader").style.padding = "0px";
document.getElementById("siteheader").style.fontWeight = "800";
document.getElementById("siteheader").style.fontSize = "1.2rem";
document.getElementById("siteheader").style.display = "flex";
document.getElementById("siteheader").style.flexFlow = "column-reverse";
document.getElementById("siteheader").style.alignItems = "center";
//small tweaks to save space on existing elements
document.getElementsByClassName("school")[0].style.margin = "2px 12px 0 12px";
document.getElementsByClassName("school")[0].style.lineHeight = 1;
document.getElementsByClassName("margin")[0].style.padding = "0px 10px";
siteHeader.innerHTML = '';
siteHeader.style.width = "100%";
siteHeader.append(modifiedHeader);
window.scroll(0,0);
}
modifiedHeader.style.display='flex'
modifiedHeader.style.justifyContent = 'space-around';
modifiedHeader.style.width = "100%";
modifiedHeader.style.fontSize = "1.2rem";
return document.getElementById("siteheader");
}
function masterTimetable(newHeader) {
const dateForm = document.getElementsByTagName("form")[0];
const dateChanger = document.createElement("form");
for(const atr of ["name", "method", "action"]){
dateChanger.setAttribute(atr,dateForm.getAttribute(atr));
}
const dateChangerParts = new Set(["INPUT"]);
for(var i=0 ;i<dateForm.children.length; i++){
if(dateChangerParts.has(dateForm.children[i].tagName)){dateChanger.append(dateForm.children[i].cloneNode(true));}
}
dateChanger.style.display="inline-block";
document.body.style.display="flex";
document.body.style.flexDirection="column";
const headerText = document.getElementById("siteheader").innerText;
const reNum = /(?<=Week\s)\d\d?/;
const weekno = headerText.match(reNum)[0];
const reName = /(Week\s)\d\d?/;
const weekName = headerText.match(reName)[0];
const date = new Date();
const day = date.getDay();
const extractData = (tableId, mapper) => {
const myTab = document.getElementById(tableId);
if (myTab) {
const data = [...myTab.rows].map((r) => [...r.cells].map((c) => c.innerHTML));
return data.map(mapper);
}
};
const data = extractData('DataTables_Table_0', (x) => (x));
var datePicker = document.getElementsByTagName("form")[0];
if(!data){
console.log("Error getting timetable - no data exists");
}
else{
document.getElementById("root").style.display="none";
document.body.append(newHeader);
const toggleChangesBtn = document.createElement("button");
toggleChangesBtn.innerText = " X - Close Faculty View";
toggleChangesBtn.style.position="fixed";
toggleChangesBtn.style.top="4px";
toggleChangesBtn.style.left="45px";
toggleChangesBtn.addEventListener("click", toggleChanges);
document.body.append(toggleChangesBtn);
function toggleChanges(){
document.getElementById("root").style.display="block";
document.getElementById("gridTimetable").style.display = "none";
toggleChangesBtn.style.display="none";
}
newHeader.insertAdjacentElement("afterBegin", dateChanger);
var side = 0;
var groupOne = ["Teacher"];
for(var item of userData.names){
groupOne.push(item.name);
}
var groupTwo = ["Teacher"];
for(var itemTwo of userData.namesTwo){
groupTwo.push(itemTwo.name);
}
function checkNames(names,input){
for(var name of names){
if(input.includes(name)){
return true;
}
}
return false;
}
function switchSides(){
document.getElementById("gridTimetable").remove();
if(side == 0){
makeTimetable(data,groupTwo);
}
else{
makeTimetable(data, groupOne);
}
side = 1-side;
}
if(side == 0){
makeTimetable(data, groupOne);
}
else{
makeTimetable(data, groupTwo);
}
function addSwitchButton(){
var label = "1️⃣|2️⃣";
var corner = document.getElementById("cell0-0");
var cell = document.getElementById("cell0-5");
cell.innerText="P2";
var btnOne = document.createElement("button");
btnOne.innerHTML=(`${label}`);
btnOne.addEventListener("click", switchSides);
corner.append(btnOne);
}
function makeTimetable(data, group){
var gridContainer = document.createElement("div");
gridContainer.id="gridTimetable";
gridContainer.style.fontSize = "1.4rem";
gridContainer.style.display = "grid";
//Need to count teachers found:
var nameCount = 0;
for(let x = 0; x<data.length; x++){
if(checkNames(group, data[x][0])){
nameCount+=1;
}
}
gridContainer.style.gridTemplateColumns = `4rem repeat(${nameCount-1}, 1fr)`;
document.body.parentElement.style.height="100%"
document.body.style.height="100%";
gridContainer.style.height = "100%";
document.body.append(gridContainer);
// Original loop used to display table data
for(let y=0; y<data[0].length-1;y++){
for(let x = 0; x<data.length; x++){
// These numebrs seem arbitrary - they are the columns corresponding to the relevant periods on Millennium
if(checkNames(group, data[x][0])){
if(y===0 || y===3 || y===5|| y===8|| y===9|| y===13|| y===15|| y===18|| y===19|| y===21|| y===22){
if(data[x][7].length > 4){
data[x][8] = data[x][7];
}
if(data[x][11].length > 4){
data[x][9] = data[x][11];
}
if(data[x][17].length > 4){
data[x][18] = data[x][17];
}
if(data[x][20].length > 4){
data[x][19] = data[x][20];
}
// Sometimes senior classes sit in the duplicate P1 or P2 column.
//These two if statements fix this issue by copying the value into the usual P1 or P2 column.
if(y==3){
if(data[x][4].length>4){
data[x][3]=data[x][4];
}
}
if(y==5){
if(data[x][6].length>4){
data[x][5]=data[x][6];
}
}
var tempDiv = document.createElement("div");
tempDiv.id=`cell${x}-${y}`;
tempDiv.style.boxSizing = "border-box";
tempDiv.style.backgroundColor = "#858585";
tempDiv.style.border = "1px solid black";
tempDiv.style.margin = "0px";
tempDiv.style.padding = "0px";
//tempDiv.style.height = "8%";
tempDiv.innerHTML = `${data[x][y]}`;//`${x},${y} - ${data[x][y]}`;
tempDiv.style.display="flex";
tempDiv.style.flexDirection="column";
tempDiv.style.justifyContent="center";
tempDiv.style.alignItems="center";
tempDiv.style.textAlign="center";
if(y==0 || x==0){
tempDiv.style.backgroundColor="#a4f1ff";
}
if(x+y==0){
tempDiv.innerHTML = "";
tempDiv.style.backgroundColor="#858585";
}
if(y==0||y==5||y==9||y==15||y==19){
tempDiv.style.borderBottom = "5px solid black";
}
if(x==0){
tempDiv.style.borderRight = "5px solid black";
}
if(y==0){
tempDiv.style.fontWeight = "600";
}
if(y>0){
if(x==0){
if(data[0][y].includes("a")||data[0][y].includes("b")){
tempDiv.style.backgroundColor="hsl(0deg 65% 70%)";
}}}
// Add conditional formatting for periods based on contents
if(data[x][y].length===0){
tempDiv.style.backgroundColor="#eee";
}
if(data[x][y].includes("Duty")){
tempDiv.style.backgroundColor="hsl(0deg 70% 60%)";
}
else if(data[x][y].length>4){
if(y>0){
var yr = data[x][y][0]; // Colour by grade using first character
if(yr==7){yr=0}
else if(yr==8){yr=1}
else if(yr==9){yr=2}
else if(yr==1){yr=3}
else if(yr=="P"){yr=4}
else if(yr=="H"){yr=5}
tempDiv.style.backgroundColor = `hsl(${yr*60}deg 65% 80%)`;
}}
gridContainer.append(tempDiv);
}
}
}
}
addSwitchButton();
}
}
}// End of Master Timetable script
function usePhotoRoll(){
var theCheckbox=document.querySelector('input[name="hideCodes"]');
document.querySelector('input[name="hideCodes"]').parentElement.id = "photoRollCheckbox";
document.querySelector('input[name="hideCodes"]').parentElement.innerText=" Use photo roll";
document.getElementById("photoRollCheckbox").insertAdjacentElement("afterbegin",theCheckbox);
if(document.querySelector('input[name="hideCodes"]').checked){
function areaOne(el){
el.style.gridColumnStart="1";
el.style.gridColumnEnd="4";
}
function areaTwo(el){
el.style.gridColumnStart="4";
el.style.gridColumnEnd="5";
}
function areaThree(el){
el.style.gridColumnStart="1";
el.style.gridColumnEnd="3";
}
function areaFour(el){
el.style.gridColumnStart="3";
el.style.gridColumnEnd="5";
}
function stretch(el){
el.style.gridColumnStart="1";
el.style.gridColumnEnd="5";
}
var theTable=document.getElementsByClassName("table1sm")[0].children[0];
for(var notes of document.querySelectorAll('div.editnotes')){
notes.style.width="200px";
notes.style.display="inline-block";
notes.style.background="#fff";
}
for(var studentName of document.querySelectorAll("a.student")){
studentName.parentElement.style.fontSize="1.2rem";
studentName.insertAdjacentElement("beforebegin",document.createElement("br"));
}
for(var pic of document.querySelectorAll('img.studentPhoto')){
pic.style.height="150px";
}
for(var i=1;i<theTable.children.length-1;i++){
theTable.children[i].style.border="2px solid grey";
theTable.children[i].style.borderRadius="8px";
let ouLabel = document.createElement("span");
ouLabel.innerText = "Out of Uniform";
let tbLabel = document.createElement("span");
tbLabel.innerText = "Toilet Break";
let noteLabel = document.createElement("span");
noteLabel.innerText = "Notes ";
theTable.children[i].children[7].insertAdjacentElement("afterbegin", ouLabel);
theTable.children[i].children[11].insertAdjacentElement("afterbegin", tbLabel);
theTable.children[i].children[13].insertAdjacentElement("afterbegin", noteLabel);
stretch(theTable.children[i].children[0]);
theTable.children[i].children[0].style.textWrap="wrap";
theTable.children[i].children[0].style.textAlign="center";
stretch(theTable.children[i].children[1]);
stretch(theTable.children[i].children[4]);
areaThree(theTable.children[i].children[5]);
areaFour(theTable.children[i].children[6]);
areaThree(theTable.children[i].children[7]);
areaFour(theTable.children[i].children[11]);
stretch(theTable.children[i].children[13]);
stretch(theTable.children[i].children[14]);
areaOne(theTable.children[i].children[15]);
areaTwo(theTable.children[i].children[16]);
}
for(var i=0;i<theTable.children.length-1;i++){
var offset = 0;
for(var j=0;j<theTable.children[i].children.length+offset;j++){
if([2,3,8,9,10,12].includes(j)){
theTable.children[i].children[j-offset].remove();
offset+=1;
}
}
theTable.children[i].style.display='grid';
theTable.children[i].style.justifyItems='center';
theTable.children[i].style.justifyContent='center';
theTable.children[i].style.gridTemplateColumns='1fr 1fr 1fr 32px';
}
theTable.children[0].remove();
theTable.children[theTable.children.length-1].style.gridColumnStart="1";
theTable.children[theTable.children.length-1].style.gridColumnEnd="3";
theTable.style.display='grid';
theTable.style.justifyContent = 'center';
theTable.style.gap="4px";
theTable.style.gridTemplateColumns='repeat(auto-fill, 270px)';
theTable.style.width="calc(100% + 40px)";
theTable.style.transform = "translateX(-12px)";
}
} // End of photo roll function
var styles = ``;
var compactDashboardStyles = `/* Hide content and additional links for each card
-> Leave only header for class name/teacher*/
.TQYOZc, .SZ0kZe{
display:none !important;
}
/*Change size of cards*/
.gHz6xd{
height: 4.5rem !important;
width: 18% important;
}
/*Remove padding around titles*/
.R4EiSb{
padding-left:4px important;
padding-top: 2px important;
padding: 0.5rem 1rem 0rem important;
padding-right: 0px important;
}
/*Allow cards to go right ot edges of screen*/
.JwPp0e{
padding-left: 0rem !important;
}
/*Reduce gaps between cards*/
.gHz6xd{
margin: 4px !important;
}
/* Resize nav pane on left
.Tabkde, .ideBx{
width: 4em important;
}
.ideBx{
flex: 0 0 6rem important;
}
*/
/*Remove elipses (...) from long class names */
.YVvGBb{
text-overflow: clip !important;
}
.topRightButton{
position: absolute !important;
top: 0 !important;
right:6px !important;
}
/*
.VfPpkd-Bz112c-LgbsSe{
width:45px important;
}*/
`;
const hideGCbannerImageStyle = `
/*Remove header images */
.OjOEXb{
background-image: none !important;
}`;
function hideOtherClasses(){
var currentURL = window.location.href;
// Needs to not run on Class page - 'Grades' link disappears otherwise??
if(!currentURL.includes('/c/')){
var ClassList = [];
for(var classCodeStub of userData.gClassroomCodes){
ClassList.push(`/c/${classCodeStub}`);
}
//["/c/NjM2NTI5NzUxNjY4", "/c/NjM2NTAzNDY3MDc0", "/c/NTMwNDA4Nzc2NzU5", "/c/NTMwNDA4ODg5OTE3", "/c/NTMwNDYxNjg0NDA3", "/c/Njc3MzI1NDA0MDkw", "/c/Njc4MTc0NTcxNTA3"];
var hrefList = []
const hrefPrefix = "https://classroom.google.com";
for(var classURL of ClassList){
hrefList.push(hrefPrefix+classURL);
}
var els = document.querySelectorAll("a[href^='/c/']");
for (var el of els) {
if(!hrefList.includes(el.href)){el.style.display="none";}
}
}
}
function highlightDashboardClasses(){
//Change Google Classroom URLS here to highlight on GC dashboard.
var gcClassesList = userData.gClassroomCodes;
var gcClasses = "";
var commasAdded = 0;
for(var classUrl of gcClassesList){
if(commasAdded>0){
gcClasses +=", ";
}
gcClasses+=`a[href="/c/${classUrl}"] > div:first-child`;
commasAdded+=1;
}
var hClassList = `
${gcClasses}
{
color:black !important ;
font-weight: 800 !important;
background-color: rgba(256 256 0 / 0.75);
border-radius:16px;
padding:0px 16px;
}
nav a > div{
background: none !important;
}
`
return hClassList;
}
var stylesRules = `
.butt{
position: absolute;
top: 5px;
right: 40px;
z-index: 2;
background-color: lime;
}
@media print{
.butt{
display: none !important;
}
}
.popup, .popup p, .popup input{
font-size:1.1rem;
}
.popup{
border-radius: 32px;
border: 6px solid grey;
background: antiquewhite;
max-height:90%;
}
.popup h2, .popup h3{
color: green;
}
.popupcontainer{
display: flex;
flex-direction: column;
align-items: center;
}
.popupcontainer > form{
width: 100%;
}
.popuprow{
width: 100%;
display: flex;
flex-direction: row;
min-height: 1.5rem;
justify-content: space-between;
align-items: center;
}
::backdrop {
background-image: linear-gradient(
45deg,
dodgerblue,
green
);
opacity: 0.75;
}
#saveSettings{
background-color:#0E4;
}
#savedIndicator{
color:green;
display:none;
}
#cell0-0, #cell0-3, #cell0-5, #cell0-8, #cell0-9, #cell0-13, #cell0-15, #cell0-18, #cell0-19, #cell0-21, #cell0-22 {
position:sticky;
left:0;
}
#gridTimetable{
overflow-x: scroll;
}`;
function addEdvalLinks(){
const theDate = checkDate();
const linkPrefix = "https://rousehillhs.edval.education/timetable#search/" + theDate + "/resourceTimetable/day/";
const mainBlock = document.getElementsByClassName("top-bar-section")[0];
const leftBlock = document.getElementsByClassName("left")[0];
addButton("Me", "https://rousehillhs.edval.education/timetable");
const links = [
{
name: "D-Block",
groups:
[
{
name: "⬆️ Upstairs",
rooms:["D101", "D104", "D109", "D110", "D112", "D113"]
},
{
name: "⬇️ Downstairs",
rooms: ["D1", "D4", "D9", "D10", "D12", "D13"]
},
]
},
{
name: "E-Block",
groups:
[
{
name: "⬆️ Upstairs",
rooms:["E101", "E104", "E109", "E110", "E112"]
},
{
name: "⬇️ Downstairs",
rooms: ["E1", "E4", "E9", "E10", "E12"]
},
]
},
{
name: "BMQ",
groups:
[
{
name: "B",
rooms:["B1", "B2", "B3", "B4", "B5", "B6"]
},
{
name: "M",
rooms: ["M1", "M2", "M3", "M4", "M5"]
},
{
name: "Q",
rooms: ["Q1", "Q2"]
},
]
}
];
for(var link of links){
var targetLink = linkPrefix;
if(link.groups){
var linkHeading = addDropdown(link.name);
for(var group of link.groups){
targetLink=linkPrefix;
if(group.staff){
targetLink += staffList(group.staff);
}
if(group.rooms){
targetLink += roomList(group.rooms);
}
addDropdownItem(linkHeading, group.name, targetLink);
}
}
else{
if(link.staff){
targetLink += staffList(link.staff);
}
if(link.rooms){
targetLink += roomList(link.rooms);
}
addButton(link.name, targetLink);
}
}
function roomList(rooms){
return "R!"+ rooms.join(",R!") + ",";
}
function staffList(staff){
return "T!"+ staff.join(",T!") + ",";
}
function checkDate(){
const today = new Date();
const yyyy = String(today.getFullYear());
var mm = String(today.getMonth()+1);
var dd = String(today.getDate());
if( mm.length == 1){
mm = "0"+mm
};
if( dd.length == 1){
dd = "0"+dd
};
const date = [dd,mm,yyyy].join('-');
return date;
}
function addDropdown(title){
var dropdown = document.createElement("li");
dropdown.classList.add("has-dropdown");
dropdown.classList.add("not-click");
var dropdownLabel = document.createElement("a");
dropdownLabel.href = "#";
dropdownLabel.innerText = title;
dropdown.appendChild(dropdownLabel);
var droppedDown = document.createElement("ul");
droppedDown.classList.add("dropdown");
dropdown.appendChild(droppedDown);
leftBlock.append(dropdown);
return droppedDown;
}
function addDropdownItem(dropdown, label, target){
var dropdownItem = document.createElement("li");
var dItemLink = document.createElement("a");
dItemLink.href = target;
dItemLink.innerText = label;
dropdownItem.appendChild(dItemLink);
dropdown.appendChild(dropdownItem);
}
function addButton(title, target){
var label = document.createElement("li");
label.classList.add("not-click");
var link = document.createElement("a");
link.innerText = title;
link.onclick = (()=>{window.location.href=target;});
label.append(link);
leftBlock.append(label);
}
}
runFunctions();
function runFunctions(){
if(r_edval.test(currentURL)){
addEdvalLinks();
}
if(r_millennium.test(currentURL)){
var cssRules = document.createElement("style");
cssRules.innerHTML = stylesRules;
document.body.append(cssRules);
addMillenniumSettings();
if(r_rollmarking.test(currentURL)){
if(userData.millenniumToggles.truancy){
try{
console.log(`Enhancing: Roll marking - Truancy email links`);
truancyEmailLinks();
}
catch(error){
console.log("Error in truancy links function");
console.log(error)
}
}
if(userData.millenniumToggles.photoRoll){
try{
console.log("Enhancing: Roll marking - Photo roll view");
usePhotoRoll();
}
catch(error){
console.log("Error in photo roll function");
console.log(error);
}
}
}
console.log(`Enhancing: Site Header`);
const newSiteHeader = enhanceSiteHeader().cloneNode(true);
if(r_timetable.test(currentURL) && userData.millenniumToggles.timetable){
try{
console.log(`Enhancing: Master timetable view`);
masterTimetable(newSiteHeader);
}
catch(error){
console.log("Error in master timetable view function");
console.log(error);
}
}
}
if(r_gClassroom.test(currentURL)){
console.log("Attempting to enhance Google Classroom");
// Required on Google Classroom due to required 'trusted HTML' policy (to prevent XSS / script injection attacks)
const escapeHTMLPolicy = trustedTypes.createPolicy("myEscapePolicy", {
createHTML: (string) => string.replace(/</g, "<"),
});
const escaped = escapeHTMLPolicy.createHTML(styles);
// Add rule to page
var css = document.createElement("style");
css.innerHTML = escaped;
document.body.append(css);
// Check that dashboard is loaded before running function
var interval = setInterval(checkClassroomLoaded, 50);
function checkClassroomLoaded(){
var links = document.getElementsByTagName("a");
if(links.length>100){
clearInterval(interval);
interval = null;
googleClassroomDashboard();
}
}
function makeButtonsVisible(){
var gcButtons = document.getElementsByClassName('VfPpkd-dgl2Hf-ppHlrf-sM5MNb');
if(window.location.href.includes('/h')){
for(var gcButton of gcButtons){
gcButton.classList.add('topRightButton');
}
}
else{
for(var gcButton of gcButtons){
gcButton.classList.remove('topRightButton');
}
}
}
var buttonInterval = setInterval(makeButtonsVisible, 3000);
function googleClassroomDashboard() {
if(userData.gClassroomToggles.condenseCards){
styles += compactDashboardStyles;
}
if(userData.gClassroomToggles.highlightClasses){
var hClassList = highlightDashboardClasses();
styles +=hClassList;
}
if(userData.gClassroomToggles.hideImages){
styles += hideGCbannerImageStyle;
}
if(userData.gClassroomToggles.hideClasses){
hideOtherClasses();
}
//Required on Google Classroom due to required 'trusted HTML' policy (to prevent XSS / script injection attacks)
const escapeHTMLPolicy = trustedTypes.createPolicy("myEscapePolicy", {
createHTML: (string) => string.replace(/</g, "<"),
});
const escaped = escapeHTMLPolicy.createHTML(styles);
//Add rule to page
var css = document.createElement("style");
css.innerHTML = escaped;
document.body.append(css);
}
}//End of Google Classroom Dashboard script
}
})();