schoolbox web extension :)
at main 170 lines 4.5 kB view raw
1import { dataAttr, injectInlineStyles, setDataAttr, uninjectInlineStyles } from "@/utils"; 2import { getCurrentPeriod } from "@/utils/periodUtils"; 3import { Plugin } from "@/utils/plugin"; 4import type { Toggle } from "@/utils/storage"; 5import type { StorageState } from "@/utils/storage/state.svelte"; 6import styleText from "./styles.css?inline"; 7 8const ID = "subheader"; 9const PLUGIN_ID = `plugin-${ID}`; 10 11let intervals: NodeJS.Timeout[] = []; 12let oldChildren: ChildNode[] = []; 13let subheader: HTMLHeadingElement | null = null; 14 15export type Settings = { 16 openInNewTab: StorageState<Toggle>; 17}; 18 19export default new Plugin<Settings>( 20 { 21 id: ID, 22 name: "Subheader Revamp", 23 description: "Adds a clock and current period info to the subheader.", 24 }, 25 true, 26 { 27 openInNewTab: { toggle: true }, 28 }, 29 async (settings) => { 30 const openInNewTab = await settings.openInNewTab.get(); 31 injectSubheader(openInNewTab.toggle); 32 }, 33 uninjectSubheader, 34 [".subheader", ".timetable"], 35); 36 37function injectSubheader(openInNewTab: boolean) { 38 // abort if plugin is injected 39 if (subheader !== null) return; 40 41 // abort if not on homepage 42 if (window.location.pathname !== "/") return; 43 44 subheader = document.querySelector("h2.subheader"); 45 if (!subheader) return; 46 47 // inject subheader styling 48 injectInlineStyles(styleText, PLUGIN_ID); 49 50 // delete all children of the subheader 51 while (subheader.firstChild) { 52 oldChildren.push(subheader.removeChild(subheader.firstChild)); 53 } 54 55 updatePeriodSpan(openInNewTab); 56 updateClockSpan(); 57 updateDateSpan(); 58 59 intervals = [ 60 setInterval(() => updatePeriodSpan(openInNewTab), 5000), 61 setInterval(updateClockSpan, 1000), 62 setInterval(updateDateSpan, 60000), 63 ]; 64} 65 66function uninjectSubheader() { 67 // abort if plugin is not injected 68 if (subheader === null) return; 69 70 // abort if not on homepage 71 if (window.location.pathname !== "/") return; 72 73 // stop updating the subheader 74 intervals.forEach((interval) => clearInterval(interval)); 75 76 // remove new children 77 while (subheader.firstChild) { 78 subheader.removeChild(subheader.firstChild); 79 } 80 81 // restore old children 82 for (const child of oldChildren) { 83 subheader.appendChild(child); 84 } 85 86 // uninject subheader styling 87 uninjectInlineStyles(PLUGIN_ID); 88 89 // reset variables 90 intervals = []; 91 oldChildren = []; 92 subheader = null; 93} 94 95async function updatePeriodSpan(openInNewTab: boolean) { 96 if (!subheader) return; 97 98 const periodId = `${PLUGIN_ID}-period`; 99 let periodSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(periodId)}`); 100 101 if (!periodSpan) { 102 periodSpan = document.createElement("span"); 103 setDataAttr(periodSpan, periodId); 104 subheader.appendChild(periodSpan); 105 } 106 107 periodSpan.textContent = ""; 108 109 // set period span content 110 const period = getCurrentPeriod(); 111 if (period) { 112 const name = period.data.name || period.header.name; 113 const room = period.data.room ? ` (${period.data.room})` : ""; 114 let periodLink = periodSpan.querySelector("a"); 115 if (period.data.name && period.data.link) { 116 // if there's period data 117 if (!periodLink) { 118 periodLink = document.createElement("a"); 119 120 periodLink.target = openInNewTab ? "_blank" : "_self"; 121 periodSpan.appendChild(periodLink); 122 } 123 periodLink.href = period.data.link; 124 periodLink.textContent = `${name}${room}`; 125 } else { 126 // if there's only the header 127 periodSpan.textContent = `${name}${room}`; 128 if (periodLink) { 129 periodSpan.removeChild(periodLink); 130 } 131 } 132 } 133} 134 135function updateClockSpan() { 136 if (!subheader) return; 137 138 const clockId = `${PLUGIN_ID}-clock`; 139 let clockSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(clockId)}`); 140 141 if (!clockSpan) { 142 clockSpan = document.createElement("span"); 143 setDataAttr(clockSpan, clockId); 144 subheader.appendChild(clockSpan); 145 } 146 147 // set clock span content 148 const date = new Date(); 149 clockSpan.textContent = date.toLocaleTimeString([], { 150 hour: "2-digit", 151 minute: "2-digit", 152 }); 153} 154 155function updateDateSpan() { 156 if (!subheader) return; 157 158 const dateId = `${PLUGIN_ID}-date`; 159 let dateSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(dateId)}`); 160 161 if (!dateSpan) { 162 dateSpan = document.createElement("span"); 163 setDataAttr(dateSpan, dateId); 164 subheader.appendChild(dateSpan); 165 } 166 167 // set date span content 168 const date = new Date(); 169 dateSpan.textContent = date.toDateString(); 170}