mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-13 23:24:40 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b503363d64 | |||
| b69d5f47fc | |||
| 404d3c02f3 | |||
| 964a026e7a | |||
| c7d9e1d955 | |||
| e305b70035 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "betterseqtaplus",
|
"name": "betterseqtaplus",
|
||||||
"version": "3.4.7",
|
"version": "3.4.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
|
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
|
||||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import browser from "webextension-polyfill";
|
|||||||
import * as plugins from "@/plugins";
|
import * as plugins from "@/plugins";
|
||||||
import { main } from "@/seqta/main";
|
import { main } from "@/seqta/main";
|
||||||
import { delay } from "./seqta/utils/delay";
|
import { delay } from "./seqta/utils/delay";
|
||||||
|
import { initializeHideSensitiveToggle } from "@/seqta/utils/hideSensitiveToggle";
|
||||||
|
|
||||||
export let MenuOptionsOpen = false;
|
export let MenuOptionsOpen = false;
|
||||||
|
|
||||||
@@ -70,6 +71,8 @@ async function init() {
|
|||||||
await plugins.initializePlugins();
|
await plugins.initializePlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeHideSensitiveToggle();
|
||||||
|
|
||||||
console.info(
|
console.info(
|
||||||
"[BetterSEQTA+] Successfully initialised BetterSEQTA+, starting to load assets.",
|
"[BetterSEQTA+] Successfully initialised BetterSEQTA+, starting to load assets.",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -94,3 +94,57 @@ body:has(.outside-container:not(.hide))
|
|||||||
background: var(--text-primary) !important;
|
background: var(--text-primary) !important;
|
||||||
color: var(--theme-primary) !important;
|
color: var(--theme-primary) !important;
|
||||||
}
|
}
|
||||||
|
.fixed-tooltip {
|
||||||
|
display: inline-block;
|
||||||
|
z-index: 5 !important;
|
||||||
|
width: 28px;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 2px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.fixed-tooltip svg {
|
||||||
|
fill: var(--theme-primary);
|
||||||
|
}
|
||||||
|
.tooltiptext-fixed {
|
||||||
|
width: 120px;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
transform-origin: top;
|
||||||
|
background: var(--background-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -62px;
|
||||||
|
}
|
||||||
|
.tooltiptext-fixed::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent transparent var(--text-primary) transparent;
|
||||||
|
}
|
||||||
|
.tooltiptext-fixed.show {
|
||||||
|
transform: scale(1);
|
||||||
|
transform-origin: top;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.tooltiptext-fixed p:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(0, 0, 0, 0.3) !important;
|
||||||
|
transition: 200ms;
|
||||||
|
}
|
||||||
|
.tooltiptext-fixed p {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1256,6 +1256,9 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
.customshortcut > svg {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
.colourbar {
|
.colourbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
@@ -2730,12 +2733,13 @@ body {
|
|||||||
.menuShown #menuToggle .hamburger-line:nth-child(3) {
|
.menuShown #menuToggle .hamburger-line:nth-child(3) {
|
||||||
transform: translateY(-6px) rotate(-45deg);
|
transform: translateY(-6px) rotate(-45deg);
|
||||||
}
|
}
|
||||||
.day-empty {
|
div.day-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 15em;
|
height: 15em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 16px 0;
|
border-radius: 16px 0;
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
import type { SettingsList } from "@/interface/types/SettingsProps"
|
import type { SettingsList } from "@/interface/types/SettingsProps"
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
||||||
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
|
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
|
||||||
import hideSensitiveContent from "@/seqta/ui/dev/hideSensitiveContent"
|
|
||||||
|
|
||||||
import { getAllPluginSettings } from "@/plugins"
|
import { getAllPluginSettings } from "@/plugins"
|
||||||
import type { BooleanSetting, StringSetting, NumberSetting, SelectSetting, ButtonSetting, HotkeySetting, ComponentSetting } from "@/plugins/core/types"
|
import type { BooleanSetting, StringSetting, NumberSetting, SelectSetting, ButtonSetting, HotkeySetting, ComponentSetting } from "@/plugins/core/types"
|
||||||
@@ -322,9 +321,9 @@
|
|||||||
<p class="text-xs">Replace sensitive content with mock data</p>
|
<p class="text-xs">Replace sensitive content with mock data</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Switch
|
||||||
onClick={() => hideSensitiveContent()}
|
state={$settingsState.hideSensitiveContent ?? false}
|
||||||
text="Hide"
|
onChange={(isOn: boolean) => settingsState.hideSensitiveContent = isOn}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ interface PrefItem {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||||
|
import { getMockAssessmentsData } from "@/seqta/ui/dev/hideSensitiveContent";
|
||||||
|
|
||||||
let cache: { time: number; data: any } | null = null;
|
let cache: { time: number; data: any } | null = null;
|
||||||
const CACHE_MS = 10 * 60 * 1000;
|
const CACHE_MS = 10 * 60 * 1000;
|
||||||
const student = 69;
|
const student = 69;
|
||||||
@@ -102,6 +105,10 @@ async function loadSubmissions(student: number, assessments: any[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getAssessmentsData() {
|
export async function getAssessmentsData() {
|
||||||
|
if (settingsState.mockNotices) {
|
||||||
|
return getMockAssessmentsData();
|
||||||
|
}
|
||||||
|
|
||||||
if (cache && Date.now() - cache.time < CACHE_MS) return cache.data;
|
if (cache && Date.now() - cache.time < CACHE_MS) return cache.data;
|
||||||
const [subjects, colors, upcoming] = await Promise.all([
|
const [subjects, colors, upcoming] = await Promise.all([
|
||||||
loadSubjects(),
|
loadSubjects(),
|
||||||
|
|||||||
Binary file not shown.
@@ -7,6 +7,21 @@ interface ContentConfig {
|
|||||||
[key: string]: ElementConfig;
|
[key: string]: ElementConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track processed elements to avoid re-randomizing
|
||||||
|
const processedElements = new WeakSet<Element>();
|
||||||
|
|
||||||
|
function debounce(func: Function, wait: number): Function {
|
||||||
|
let timeout: NodeJS.Timeout;
|
||||||
|
return function executedFunction(...args: any[]) {
|
||||||
|
const later = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
func(...args);
|
||||||
|
};
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getRandomElement(array: string[]): string {
|
function getRandomElement(array: string[]): string {
|
||||||
return array[Math.floor(Math.random() * array.length)];
|
return array[Math.floor(Math.random() * array.length)];
|
||||||
}
|
}
|
||||||
@@ -164,9 +179,32 @@ const contentConfig: ContentConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
forumTopics: {
|
forumTopics: {
|
||||||
selector: "#menu .sub ul li label",
|
selector: "#menu .sub ul li:not([data-colour]):not(.hasChildren) label",
|
||||||
action: (element) => {
|
action: (element) => {
|
||||||
element.textContent = "Forum Topic Redacted";
|
// Only redact if not in assessments section
|
||||||
|
const assessmentsSection = element.closest('[data-key="assessments"]');
|
||||||
|
if (!assessmentsSection) {
|
||||||
|
element.textContent = "Forum Topic Redacted";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assessmentSubjects: {
|
||||||
|
selector: '[data-key="assessments"] .sub ul li[data-colour] label',
|
||||||
|
action: (element) => {
|
||||||
|
element.textContent = getRandomElement(mockData.subjects);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assessmentYearGroups: {
|
||||||
|
selector: '[data-key="assessments"] .sub ul li.hasChildren:not([data-colour]) label',
|
||||||
|
action: (element) => {
|
||||||
|
const yearGroup = Math.floor(Math.random() * 5) + 8; // Years 8-12
|
||||||
|
element.textContent = `Year ${yearGroup}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assessmentSubYearGroups: {
|
||||||
|
selector: '[data-key="assessments"] .sub .sub ul li[data-colour] label',
|
||||||
|
action: (element) => {
|
||||||
|
element.textContent = getRandomElement(mockData.subjects);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
courseNames: {
|
courseNames: {
|
||||||
@@ -541,11 +579,168 @@ export function getMockNotices() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function hideSensitiveContent() {
|
export function getMockAssessmentsData() {
|
||||||
|
const subjects = mockData.subjects.slice(0, 5).map((title, i) => ({
|
||||||
|
code: `SUBJ${i + 1}`,
|
||||||
|
programme: i + 1,
|
||||||
|
metaclass: i + 1,
|
||||||
|
title,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const colors: Record<string, string> = {};
|
||||||
|
subjects.forEach((s) => {
|
||||||
|
colors[s.code] = `hsl(${Math.floor(Math.random() * 360)},70%,60%)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusTemplates = [
|
||||||
|
// Marked with scores (70-90%) - goes to MARKS_RELEASED
|
||||||
|
{ submitted: true, score: () => Math.floor(Math.random() * 21) + 70, dayOffset: () => Math.floor(Math.random() * -30) - 7 }, // Past due, marked with score
|
||||||
|
{ submitted: true, score: () => Math.floor(Math.random() * 21) + 70, dayOffset: () => Math.floor(Math.random() * -14) - 1 }, // Recently marked with score
|
||||||
|
{ submitted: true, score: () => Math.floor(Math.random() * 21) + 70, dayOffset: () => Math.floor(Math.random() * -7) }, // Very recently marked with score
|
||||||
|
|
||||||
|
// Submitted but unmarked - goes to SUBMITTED
|
||||||
|
{ submitted: true, score: null, dayOffset: () => Math.floor(Math.random() * -5) - 1 }, // Recently submitted, awaiting marking
|
||||||
|
{ submitted: true, score: null, dayOffset: () => Math.floor(Math.random() * -3) }, // Very recently submitted, awaiting marking
|
||||||
|
{ submitted: true, score: null, dayOffset: () => Math.floor(Math.random() * -2) }, // Just submitted, awaiting marking
|
||||||
|
|
||||||
|
// Due soon (not submitted) - only a couple
|
||||||
|
{ submitted: false, score: null, dayOffset: () => 0 }, // Due today
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * 3) + 2 }, // Due in next few days
|
||||||
|
|
||||||
|
// Due later (not submitted) - most assessments
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * 7) + 8 }, // Due in 1-2 weeks
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * 14) + 14 }, // Due in 2-4 weeks
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * 21) + 21 }, // Due in 3-6 weeks
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * 14) + 35 }, // Due in 5-7 weeks
|
||||||
|
|
||||||
|
// Few overdue (not submitted) - less common
|
||||||
|
{ submitted: false, score: null, dayOffset: () => Math.floor(Math.random() * -3) - 1 }, // Recently overdue
|
||||||
|
];
|
||||||
|
|
||||||
|
const assessments = Array.from({ length: 12 }, (_, i) => {
|
||||||
|
const subj = subjects[i % subjects.length];
|
||||||
|
const template = statusTemplates[i % statusTemplates.length];
|
||||||
|
const due = new Date();
|
||||||
|
due.setDate(due.getDate() + template.dayOffset());
|
||||||
|
|
||||||
|
const assessment: any = {
|
||||||
|
id: i + 1,
|
||||||
|
title: mockData.assessmentTitles[i % mockData.assessmentTitles.length],
|
||||||
|
code: subj.code,
|
||||||
|
programmeID: subj.programme,
|
||||||
|
metaclassID: subj.metaclass,
|
||||||
|
due: due.toISOString(),
|
||||||
|
submitted: template.submitted,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (template.score && typeof template.score === 'function') {
|
||||||
|
assessment.percentage = template.score(); // This triggers MARKS_RELEASED
|
||||||
|
assessment.results = {
|
||||||
|
percentage: template.score() // This displays the thermometer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return assessment;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { assessments, subjects, colors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a debounced processing function
|
||||||
|
const debouncedProcessElements = debounce(processNewElements, 1);
|
||||||
|
|
||||||
|
function processNewElements() {
|
||||||
Object.entries(contentConfig).forEach(([_, { selector, action }]) => {
|
Object.entries(contentConfig).forEach(([_, { selector, action }]) => {
|
||||||
const elements = document.querySelectorAll(selector);
|
const elements = document.querySelectorAll(selector);
|
||||||
elements.forEach((element: Element) => {
|
elements.forEach((element: Element) => {
|
||||||
action(element);
|
// Only process elements that haven't been processed before
|
||||||
|
if (!processedElements.has(element)) {
|
||||||
|
action(element);
|
||||||
|
processedElements.add(element);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let observer: MutationObserver | null = null;
|
||||||
|
let intervalId: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
export default function hideSensitiveContent() {
|
||||||
|
// Initial processing of existing elements
|
||||||
|
processNewElements();
|
||||||
|
|
||||||
|
// Set up MutationObserver if not already created
|
||||||
|
if (!observer) {
|
||||||
|
observer = new MutationObserver((mutations) => {
|
||||||
|
let shouldProcess = false;
|
||||||
|
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
// Check for both childList and subtree changes
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
// Check added nodes
|
||||||
|
if (mutation.addedNodes.length > 0) {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as Element;
|
||||||
|
// Check if the added element or its children match any of our selectors
|
||||||
|
for (const config of Object.values(contentConfig)) {
|
||||||
|
if (element.matches?.(config.selector) || element.querySelector?.(config.selector)) {
|
||||||
|
shouldProcess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also trigger on large DOM replacements (like page navigation)
|
||||||
|
if (mutation.addedNodes.length > 5 || mutation.removedNodes.length > 5) {
|
||||||
|
shouldProcess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for attribute changes that might affect our selectors
|
||||||
|
if (mutation.type === 'attributes') {
|
||||||
|
const target = mutation.target as Element;
|
||||||
|
for (const config of Object.values(contentConfig)) {
|
||||||
|
if (target.matches?.(config.selector)) {
|
||||||
|
shouldProcess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldProcess) {
|
||||||
|
debouncedProcessElements();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start observing with more comprehensive options
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['class', 'id'] // Watch for class/id changes that might affect our selectors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: periodic check for new elements (especially useful for SPA navigation)
|
||||||
|
if (!intervalId) {
|
||||||
|
intervalId = setInterval(() => {
|
||||||
|
debouncedProcessElements();
|
||||||
|
}, 500); // Check every 500ms as a fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to stop observing (useful for cleanup)
|
||||||
|
export function stopHidingSensitiveContent() {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
observer = null;
|
||||||
|
}
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustom
|
|||||||
import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement";
|
import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement";
|
||||||
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
||||||
import { getMockNotices } from "@/seqta/ui/dev/hideSensitiveContent";
|
import { getMockNotices } from "@/seqta/ui/dev/hideSensitiveContent";
|
||||||
|
import { setupFixedTooltips } from "@/seqta/utils/fixedTooltip";
|
||||||
|
|
||||||
let LessonInterval: any;
|
let LessonInterval: any;
|
||||||
let currentSelectedDate = new Date();
|
let currentSelectedDate = new Date();
|
||||||
@@ -628,12 +629,13 @@ function openNoticeModal(
|
|||||||
// Get the current scale applied to the source element and compensate for it
|
// Get the current scale applied to the source element and compensate for it
|
||||||
const computedStyle = getComputedStyle(sourceElement);
|
const computedStyle = getComputedStyle(sourceElement);
|
||||||
const transform = computedStyle.transform;
|
const transform = computedStyle.transform;
|
||||||
let scaleX = 1, scaleY = 1;
|
let scaleX = 1,
|
||||||
|
scaleY = 1;
|
||||||
if (transform && transform !== 'none') {
|
|
||||||
|
if (transform && transform !== "none") {
|
||||||
const matrix = transform.match(/matrix.*\((.+)\)/);
|
const matrix = transform.match(/matrix.*\((.+)\)/);
|
||||||
if (matrix) {
|
if (matrix) {
|
||||||
const values = matrix[1].split(', ');
|
const values = matrix[1].split(", ");
|
||||||
scaleX = parseFloat(values[0]);
|
scaleX = parseFloat(values[0]);
|
||||||
scaleY = parseFloat(values[3]);
|
scaleY = parseFloat(values[3]);
|
||||||
}
|
}
|
||||||
@@ -642,11 +644,11 @@ function openNoticeModal(
|
|||||||
// Apply inverse scale to get true original dimensions and positions
|
// Apply inverse scale to get true original dimensions and positions
|
||||||
const newSourceWidth = newSourceRect.width / scaleX;
|
const newSourceWidth = newSourceRect.width / scaleX;
|
||||||
const newSourceHeight = newSourceRect.height / scaleY;
|
const newSourceHeight = newSourceRect.height / scaleY;
|
||||||
|
|
||||||
// Calculate position shift due to center-based scaling
|
// Calculate position shift due to center-based scaling
|
||||||
const deltaX = (newSourceWidth - newSourceRect.width) / 2;
|
const deltaX = (newSourceWidth - newSourceRect.width) / 2;
|
||||||
const deltaY = (newSourceHeight - newSourceRect.height) / 2;
|
const deltaY = (newSourceHeight - newSourceRect.height) / 2;
|
||||||
|
|
||||||
const newSourceLeft = newSourceRect.left - deltaX;
|
const newSourceLeft = newSourceRect.left - deltaX;
|
||||||
const newSourceTop = newSourceRect.top - deltaY;
|
const newSourceTop = newSourceRect.top - deltaY;
|
||||||
|
|
||||||
@@ -965,7 +967,7 @@ function makeLessonDiv(lesson: any, num: number) {
|
|||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
lessonString += `
|
lessonString += `
|
||||||
<div class="tooltip assessmenttooltip">
|
<div class="fixed-tooltip assessmenttooltip">
|
||||||
<svg style="width:28px;height:28px;border-radius:0;" viewBox="0 0 24 24">
|
<svg style="width:28px;height:28px;border-radius:0;" viewBox="0 0 24 24">
|
||||||
<path fill="#ed3939" d="M16 2H4C2.9 2 2 2.9 2 4V20C2 21.11 2.9 22 4 22H16C17.11 22 18 21.11 18 20V4C18 2.9 17.11 2 16 2M16 20H4V4H6V12L8.5 9.75L11 12V4H16V20M20 15H22V17H20V15M22 7V13H20V7H22Z" />
|
<path fill="#ed3939" d="M16 2H4C2.9 2 2 2.9 2 4V20C2 21.11 2.9 22 4 22H16C17.11 22 18 21.11 18 20V4C18 2.9 17.11 2 16 2M16 20H4V4H6V12L8.5 9.75L11 12V4H16V20M20 15H22V17H20V15M22 7V13H20V7H22Z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -975,8 +977,9 @@ function makeLessonDiv(lesson: any, num: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lessonString += "</div>";
|
lessonString += "</div>";
|
||||||
|
const element = stringToHTML(lessonString);
|
||||||
return stringToHTML(lessonString);
|
setupFixedTooltips(element);
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAssessmentURL(programmeID: any, metaID: any, itemID = "") {
|
function buildAssessmentURL(programmeID: any, metaID: any, itemID = "") {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function OpenWhatsNewPopup() {
|
|||||||
let imagecont = document.createElement("div");
|
let imagecont = document.createElement("div");
|
||||||
imagecont.classList.add("whatsnewImgContainer");
|
imagecont.classList.add("whatsnewImgContainer");
|
||||||
|
|
||||||
/* let video = document.createElement("video");
|
let video = document.createElement("video");
|
||||||
let source = document.createElement("source");
|
let source = document.createElement("source");
|
||||||
|
|
||||||
source.setAttribute(
|
source.setAttribute(
|
||||||
@@ -53,19 +53,34 @@ export function OpenWhatsNewPopup() {
|
|||||||
video.loop = true;
|
video.loop = true;
|
||||||
video.appendChild(source);
|
video.appendChild(source);
|
||||||
video.classList.add("whatsnewImg");
|
video.classList.add("whatsnewImg");
|
||||||
imagecont.appendChild(video); */
|
imagecont.appendChild(video);
|
||||||
|
|
||||||
let whatsnewimg = document.createElement("img");
|
/* let whatsnewimg = document.createElement("img");
|
||||||
//whatsnewimg.src = "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-image.webp";
|
//whatsnewimg.src = "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-image.webp";
|
||||||
whatsnewimg.src = browser.runtime.getURL('../../resources/update-image.webp');
|
whatsnewimg.src = browser.runtime.getURL('../../resources/update-image.webp');
|
||||||
whatsnewimg.classList.add("whatsnewImg");
|
whatsnewimg.classList.add("whatsnewImg");
|
||||||
imagecont.appendChild(whatsnewimg);
|
imagecont.appendChild(whatsnewimg); */
|
||||||
|
|
||||||
let textcontainer = document.createElement("div");
|
let textcontainer = document.createElement("div");
|
||||||
textcontainer.classList.add("whatsnewTextContainer");
|
textcontainer.classList.add("whatsnewTextContainer");
|
||||||
|
|
||||||
let text = stringToHTML(/* html */ `
|
let text = stringToHTML(/* html */ `
|
||||||
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
|
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
|
||||||
|
<h1>3.4.8 - Improvements!</h1>
|
||||||
|
<li>Added new assessments kanban overview</li>
|
||||||
|
<li>Added custom pictures</li>
|
||||||
|
<li>Added custom shortcut icons</li>
|
||||||
|
<li>Added modern and animated notices on homepage</li>
|
||||||
|
<li>Improved global search performance and bug fixes</li>
|
||||||
|
<li>Fixed sidebar icons reverting to old style after reload</li>
|
||||||
|
<li>Fixed settings popup not appearing on disabled pages</li>
|
||||||
|
<li>Fixed 12-hour time not applying correctly in timetable</li>
|
||||||
|
<li>Fixed background flickering on page load</li>
|
||||||
|
<li>Fixed homepage lessons not properly changing days</li>
|
||||||
|
<li>Performance improvements for global search</li>
|
||||||
|
<li>Performance improvements across the extension</li>
|
||||||
|
<li>Other bug fixes and improvements</li>
|
||||||
|
|
||||||
<h1>3.4.7 - Global Search</h1>
|
<h1>3.4.7 - Global Search</h1>
|
||||||
<li>Added a new global search bar (enable in settings)
|
<li>Added a new global search bar (enable in settings)
|
||||||
<span class="beta">beta</span>
|
<span class="beta">beta</span>
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
export function setupFixedTooltips(root: Document | HTMLElement = document) {
|
||||||
|
const elements = root.querySelectorAll<HTMLElement>(".fixed-tooltip");
|
||||||
|
elements.forEach((tooltip) => {
|
||||||
|
const text = tooltip.querySelector<HTMLElement>(".tooltiptext");
|
||||||
|
if (!text) return;
|
||||||
|
tooltip.removeChild(text);
|
||||||
|
text.classList.add("tooltiptext-fixed");
|
||||||
|
|
||||||
|
let hideTimeout: number | undefined;
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
if (hideTimeout) {
|
||||||
|
clearTimeout(hideTimeout);
|
||||||
|
hideTimeout = undefined;
|
||||||
|
}
|
||||||
|
document.body.appendChild(text);
|
||||||
|
const rect = tooltip.getBoundingClientRect();
|
||||||
|
text.style.left = `${rect.left + rect.width / 2}px`;
|
||||||
|
text.style.top = `${rect.bottom + 5}px`;
|
||||||
|
requestAnimationFrame(() => text.classList.add("show"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleHide = () => {
|
||||||
|
hideTimeout = window.setTimeout(() => {
|
||||||
|
text.classList.remove("show");
|
||||||
|
setTimeout(() => {
|
||||||
|
if (text.parentElement === document.body) {
|
||||||
|
document.body.removeChild(text);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
tooltip.addEventListener("mouseenter", show);
|
||||||
|
tooltip.addEventListener("mouseleave", scheduleHide);
|
||||||
|
tooltip.addEventListener("blur", scheduleHide);
|
||||||
|
tooltip.addEventListener("click", scheduleHide);
|
||||||
|
|
||||||
|
text.addEventListener("mouseenter", () => {
|
||||||
|
if (hideTimeout) {
|
||||||
|
clearTimeout(hideTimeout);
|
||||||
|
hideTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
text.addEventListener("mouseleave", scheduleHide);
|
||||||
|
text.addEventListener("click", () => {
|
||||||
|
if (hideTimeout) {
|
||||||
|
clearTimeout(hideTimeout);
|
||||||
|
hideTimeout = undefined;
|
||||||
|
}
|
||||||
|
text.classList.remove("show");
|
||||||
|
setTimeout(() => {
|
||||||
|
if (text.parentElement === document.body) {
|
||||||
|
document.body.removeChild(text);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { settingsState } from "./listeners/SettingsState";
|
||||||
|
import hideSensitiveContent from "@/seqta/ui/dev/hideSensitiveContent";
|
||||||
|
|
||||||
|
function maybeHide() {
|
||||||
|
if (settingsState.hideSensitiveContent) {
|
||||||
|
hideSensitiveContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initializeHideSensitiveToggle() {
|
||||||
|
maybeHide();
|
||||||
|
window.addEventListener("hashchange", maybeHide);
|
||||||
|
settingsState.register("hideSensitiveContent", (val) => {
|
||||||
|
if (val) {
|
||||||
|
maybeHide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ export interface SettingsState {
|
|||||||
originalDarkMode?: boolean;
|
originalDarkMode?: boolean;
|
||||||
newsSource?: string;
|
newsSource?: string;
|
||||||
mockNotices?: boolean;
|
mockNotices?: boolean;
|
||||||
|
hideSensitiveContent?: boolean;
|
||||||
|
|
||||||
// depreciated keys
|
// depreciated keys
|
||||||
animatedbk: boolean;
|
animatedbk: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user