diff --git a/css/all.css b/css/all.css index 7559619..05cfd3c 100644 --- a/css/all.css +++ b/css/all.css @@ -168,6 +168,41 @@ video.easter-egg { overflow: auto; } +.splus-modal-contents.ui-tabs { + border: none !important; + background: none !important; +} + +.splus-modal-contents select { + font-size: 16px !important; +} + +.splus-modal-contents .ui-tabs-nav { + background-color: var(--primary-color) !important; + border: none !important; + border-bottom: 1px solid var(--primary-color) !important; +} + +.splus-modal-contents.ui-tabs .ui-tab { + background-color: var(--primary-color) !important; + border: none !important; +} + +.splus-modal-contents.ui-tabs .ui-tab .ui-tabs-anchor { + color: var(--contrast-text-color) !important; + font-size: 14px; + text-decoration: underline; +} + +.splus-modal-contents.ui-tabs .ui-tab.ui-tabs-active { + background-color: var(--background-color) !important; +} + +.splus-underline-heading { + text-decoration: underline; + margin-bottom: 5px; +} + @-webkit-keyframes animatetop { from { top: -300px; @@ -276,6 +311,7 @@ video.easter-egg { display: flex; align-items: center; justify-content: space-between; + margin: 0 10px; } .settings-actions-wrapper { @@ -324,6 +360,39 @@ input[type=text].setting-item { font-size: 80%; } +.sortable-container { + display: flex; +} + +.sortable-container .sidebar-sortable { + background-color: var(--primary-color) !important; + min-height: 200px; + padding: 5px; +} + +.sortable-container .sortable-list { + margin: 10px; + width: 50%; +} + +.sortable-container .sortable-list .sortable-item::before { + content: "⡇"; + padding-right: 5px; +} + +.sortable-container .sortable-list .sortable-item { + background-color: var(--background-color); + color: var(--contrast-text-color) !important; + font-size: 14px; + padding: 7px 5px 5px 5px; + margin: 5px; + cursor: grab; +} + +.sortable-container .sortable-list .ui-sortable-helper { + cursor: grabbing; +} + .hierarchical-grading-report.show-title.interactive { padding: 10px 0 !important; } @@ -347,6 +416,14 @@ input[type=text].setting-item { padding-top: 5px !important; } +.grade-active-color { + color: #3aa406; +} + +.grade-none-color { + color: #767676; +} + .profile-picture img.imagecache-profile_sm { width: 50px !important; height: 50px !important; @@ -528,18 +605,6 @@ the selector matches inside notifications in the new notifs dropdown visibility: var(--help-center-fab-visibility) !important; } -.overdue-submissions-wrapper { - display: var(--overdue-assignments-display) !important; -} - -.upcoming-events-wrapper { - display: var(--upcoming-assignments-display) !important; -} - -.quick-access-wrapper { - display: var(--quick-access-display) !important; -} - .quick-link-wrapper { padding-top: 5px; } @@ -766,6 +831,21 @@ a._3_bfp { display: none !important; } +/* ---- Hotfix for missized timer icons ---- */ +.upcoming-list .upcoming-event { + align-items: flex-start !important; +} + +.upcoming-list .upcoming-event * { + flex-basis: fit-content; +} + +.upcoming-list .upcoming-event > .infotip.submission-infotip { + margin-top: 2rem !important; + display: var(--to-do-list-icons-display, "block"); +} +/* ---------------------------------------- */ + .splus-mark-completed-check .infotip { width: 90% !important; } diff --git a/css/grades.css b/css/grades.css index 610b32c..f7d1c58 100644 --- a/css/grades.css +++ b/css/grades.css @@ -4,14 +4,6 @@ font-size: 14px; } -.grade-active-color { - color: #3aa406; -} - -.grade-none-color { - color: #767676; -} - tr.item-row td.grade-column div.td-content-wrapper { padding: 7px 10px 5px !important; text-align: center; diff --git a/css/modern/all.css b/css/modern/all.css index 4ed6ac8..d53847d 100644 --- a/css/modern/all.css +++ b/css/modern/all.css @@ -3386,3 +3386,11 @@ variable-intellisense { [modern=true]:root #grade-comment-field { background-color: var(--primary); } + +[modern=true]:root .splus-modern-border-radius { + border-radius: var(--border-radius); +} + +[modern=true]:root .splus-modern-padding { + padding: var(--padding); +} diff --git a/css/theme-editor.css b/css/theme-editor.css index 7b1f9be..e682f1a 100644 --- a/css/theme-editor.css +++ b/css/theme-editor.css @@ -805,4 +805,8 @@ a[href].link-color { #modern-wrapper .row .input-field.col.s12.m6 { padding-top: 5px; +} + +input::placeholder { + color: rgba(42, 42, 42, 0.5); } \ No newline at end of file diff --git a/js/all-idle.js b/js/all-idle.js index 5412e44..0d5acc3 100644 --- a/js/all-idle.js +++ b/js/all-idle.js @@ -402,7 +402,11 @@ function parseSettingsHash() { if (hashes.length > 2) { setTimeout(() => { location.hash = hashes[2]; - document.getElementById(hashes[2]).parentElement.parentElement.classList.add("setting-highlight"); + let settingEntry = document.getElementById(hashes[2]).parentElement.parentElement; + let settingTab = settingEntry.parentElement; + let tabIndex = Array.from(settingTab.parentElement.children).indexOf(settingTab) - 1; + $(".splus-settings-tabs").tabs("option", "active", tabIndex); + settingEntry.classList.add("setting-highlight"); location.hash = ""; }, 500); } diff --git a/js/all.js b/js/all.js index ebdcd58..1fe59f0 100644 --- a/js/all.js +++ b/js/all.js @@ -286,6 +286,7 @@ let modals = [ { name: "@jetline0", url: "https://github.com/jetline0" }, { name: "@dsnsgithub", url: "https://github.com/dsnsgithub" }, { name: "@senoj26", url: "https://github.com/senoj26" }, + { name: "@TheThonos", url: "https://github.com/TheThonos" }, ]) }), ]), @@ -661,6 +662,11 @@ function openOptionsMenu(settingsModal) { settingsModal.body.appendChild(getModalContents()); settingsModal.element.querySelector("#open-changelog").addEventListener("click", () => openModal("changelog-modal"), { once: true }); settingsModal.element.querySelector("#open-contributors").addEventListener("click", () => openModal("contributors-modal"), { once: true }); + Setting.onShown(); + $(".splus-settings-tabs").tabs({ + active: 0, + heightStyle: "fill" + }); }); } @@ -1142,7 +1148,7 @@ async function createQuickAccess() { createSvgLogo("splus-logo-inline"), // createElement("img", ["splus-logo-inline"], { src: chrome.runtime.getURL("imgs/plus-icon.png"), title: "Provided by Schoology Plus" }), createElement("span", [], { textContent: "Quick Access" }), - createElement("a", ["quick-right-link", "splus-track-clicks"], { id: "quick-access-splus-settings", textContent: "Settings", href: "#splus-settings#setting-input-quickAccessVisibility" }) + createElement("a", ["quick-right-link", "splus-track-clicks"], { id: "quick-access-splus-settings", textContent: "Customize Sidebar", href: "#splus-settings#setting-input-sidebarSectionOrder" }) ]), createElement("div", ["date-header", "first"], {}, [ createElement("h4", [], { textContent: "Pages" }) @@ -1216,17 +1222,7 @@ async function createQuickAccess() { } } - switch (Setting.getValue("quickAccessVisibility")) { - case "belowOverdue": - rightCol.querySelector(".overdue-submissions").insertAdjacentElement("afterend", wrapper); - break; - case "bottom": - rightCol.append(wrapper); - break; - default: - rightCol.prepend(wrapper); - break; - } + rightCol.append(wrapper); } function indicateSubmittedAssignments() { @@ -1383,7 +1379,18 @@ function indicateSubmittedAssignments() { // check if reload is present and visible on page let reloadButton = upcomingList.querySelector("button.button-reset.refresh-button"); if (reloadButton && reloadButton.offsetParent !== null) { - reloadButton.addEventListener("click", () => setTimeout(indicateSubmitted, 500)); + reloadButton.addEventListener("click", () => setTimeout(() => { + indicateSubmitted(); + + try { + document.getElementById("todo")?.remove(); + let overdueHeading = document.querySelector(`${SIDEBAR_SECTIONS_MAP["Overdue"].selector} h4`); + overdueHeading?.replaceWith(createElement("h3", [], {style: {textTransform: "capitalize"}, textContent: overdueHeading.textContent.toLowerCase()})); + let upcomingHeading = document.querySelector(`${SIDEBAR_SECTIONS_MAP["Upcoming"].selector} h4`); + upcomingHeading?.replaceWith(createElement("h3", [], {style: {textTransform: "capitalize"}, textContent: upcomingHeading.textContent.toLowerCase()})); + } + catch {} + }, 500)); } else { // loaded properly // clear out old assignments from local cache which aren't relevant anymore diff --git a/js/default-themes.js b/js/default-themes.js index c35c64b..f08945d 100644 --- a/js/default-themes.js +++ b/js/default-themes.js @@ -6,6 +6,9 @@ await loadDependencies("default-themes", []); })(); +const LAUSD_THEMES = ["LAUSD Orange", "LAUSD Dark Blue", "LAUSD 2019"]; +const CLASSIC_THEMES = ["Schoology Plus", "Rainbow"] + let __defaultThemes = [ { "name": "Schoology Plus Modern Light", diff --git a/js/home.js b/js/home.js index 8f07994..77007a7 100644 --- a/js/home.js +++ b/js/home.js @@ -101,18 +101,7 @@ function formatDateAsString(date) { if (homeFeedContainer && Setting.getValue("broadcasts") !== "disabled") { (async function () { - try { - let onlineBroadcasts = await (await fetch("https://schoologypl.us/alert.json")).json(); - - let readBroadcasts = localStorage.getItem("splus-readBroadcasts"); - readBroadcasts = readBroadcasts === null ? [] : JSON.parse(readBroadcasts); - - saveBroadcasts(onlineBroadcasts.filter(b => !readBroadcasts.includes(b.id))); - } catch (err) { - // Ignore - } - - let observer = new MutationObserver(function (mutations) { + let observer = new MutationObserver(async function (mutations) { if (mutations.length == 0) { return; } @@ -121,16 +110,34 @@ if (homeFeedContainer && Setting.getValue("broadcasts") !== "disabled") { // style is set on homeFeedContainer whenever Schoology decides to unhide it (static CSS sets display: none), i.e. when it's finished loading // once this happens, we can do our thing - let unreadBroadcasts = Setting.getValue("unreadBroadcasts"); - for (let broadcast of unreadBroadcasts || []) { - if (!broadcast.expires || broadcast.expires > Date.now()) { + let unreadBroadcasts = Setting.getValue("unreadBroadcasts") || []; + let onlineBroadcasts = []; + + try { + onlineBroadcasts = await (await fetch("https://schoologypl.us/alert.json")).json(); + + let readBroadcasts = localStorage.getItem("splus-readBroadcasts"); + readBroadcasts = readBroadcasts === null ? [] : JSON.parse(readBroadcasts); + + onlineBroadcasts = onlineBroadcasts.filter(b => !readBroadcasts.includes(b.id) && !unreadBroadcasts.map(u => u.id).includes(b.id)); + } catch (err) { + // Ignore + } + + let unexpiredBroadcasts = []; + for (let broadcast of [...unreadBroadcasts, ...onlineBroadcasts]) { + if ( + (!broadcast.expires || broadcast.expires > Date.now()) + && (!broadcast.version || compareVersions(chrome.runtime.getManifest().version, broadcast.version) >= 0) + ) { feed.insertAdjacentElement("afterbegin", postFromBroadcast(broadcast)); - } else { - unreadBroadcasts.splice(unreadBroadcasts.findIndex(x => x.id == broadcast.id), 1); - Setting.setValue("unreadBroadcasts", unreadBroadcasts); + unexpiredBroadcasts.push(broadcast); } } + // remove expired broadcasts + Setting.setValue("unreadBroadcasts", unexpiredBroadcasts); + // then disconnect observer.disconnect(); }); @@ -142,9 +149,59 @@ if (homeFeedContainer && Setting.getValue("broadcasts") !== "disabled") { })(); } +function reorderSidebar() { + let sidebar = document.getElementById("right-column-inner"); + let sidebarOrder = Setting.getValue("sidebarSectionOrder"); + let excluded = sidebarOrder?.exclude || []; + let included = (Array.from(sidebarOrder?.include || [])).reverse(); + + for (let section of Array.from(SIDEBAR_SECTIONS).reverse()) { + if (!included.includes(section.name) && !excluded.includes(section.name)) { + if (section) { + let element = document.querySelector(section.selector); + if (element) { + sidebar.insertAdjacentElement("afterbegin", element); + } + } + } + } + + for (let sectionName of included) { + let section = SIDEBAR_SECTIONS_MAP[sectionName]; + if (section) { + let element = document.querySelector(section.selector); + if (element) { + sidebar.insertAdjacentElement("afterbegin", element); + } + } + } + + for (let sectionName of excluded) { + let section = SIDEBAR_SECTIONS_MAP[sectionName]; + if (section) { + let element = document.querySelector(section.selector); + if (element) { + element.style.display = "none"; + } + } + } + + try { + document.getElementById("todo")?.remove(); + let overdueHeading = document.querySelector(`${SIDEBAR_SECTIONS_MAP["Overdue"].selector} h4`); + overdueHeading?.replaceWith(createElement("h3", [], {style: {textTransform: "capitalize"}, textContent: overdueHeading.textContent.toLowerCase()})); + let upcomingHeading = document.querySelector(`${SIDEBAR_SECTIONS_MAP["Upcoming"].selector} h4`); + upcomingHeading?.replaceWith(createElement("h3", [], {style: {textTransform: "capitalize"}, textContent: upcomingHeading.textContent.toLowerCase()})); + } + catch {} +} + (function () { indicateSubmittedAssignments(); createQuickAccess(); + setTimeout(() => { + reorderSidebar(); + }, 500); })(); Logger.debug("Finished loading home.js"); diff --git a/js/preload.js b/js/preload.js index c9de872..09c2f2d 100644 --- a/js/preload.js +++ b/js/preload.js @@ -20,6 +20,34 @@ var beta_tests = { var defaultCourseIconUrlRegex = /\/sites\/[a-zA-Z0-9_-]+\/themes\/[%a-zA-Z0-9_-]+\/images\/course-default.(?:svg|png|jpe?g|gif)(\?[a-zA-Z0-9_%-]+(=[a-zA-Z0-9_%-]+)?(&[a-zA-Z0-9_%-]+(=[a-zA-Z0-9_%-]+)?)*)?$/; +var SIDEBAR_SECTIONS = [ + { + name: "Quick Access", + selector: "#right-column-inner div.quick-access-wrapper" + }, + { + name: "Reminders", + selector: "#right-column-inner div.reminders-wrapper" + }, + { + name: "Overdue", + selector: "#right-column-inner div#overdue-submissions.overdue-submissions-wrapper" + }, + { + name: "Upcoming", + selector: "#right-column-inner div.upcoming-submissions-wrapper" + }, + { + name: "Upcoming Events", + selector: "#right-column-inner div#upcoming-events.upcoming-events-wrapper" + }, + { + name: "Recently Completed", + selector: "#right-column-inner div.recently-completed-wrapper" + }, +]; +var SIDEBAR_SECTIONS_MAP = Object.fromEntries(SIDEBAR_SECTIONS.map(s => [s.name, s])); + // Functions /** @type {HTMLDivElement} */ @@ -431,425 +459,481 @@ function updateSettings(callback) { let noControl = document.createElement("div"); modalContents = createElement("div", [], undefined, [ - createElement("div", ["splus-modal-contents"], {}, [ - new Setting( - "theme", - "Theme", - "Click to open the theme editor to create, edit, or select a theme", - "Schoology Plus", - "button", - {}, - value => value || "Schoology Plus", - event => location.href = chrome.runtime.getURL("/theme-editor.html"), - element => element.value - ).control, - new Setting( - "notifications", - "Desktop Notifications", - "Displays desktop notifications and a number badge on the extension button when new grades are entered", - "enabled", - "select", - { - options: [ - { - text: "Enable All Notifications", - value: "enabled" - }, - { - text: "Number Badge Only (No Pop-Ups)", - value: "badge" - }, - { - text: "Pop-Ups Only (No Badge)", - value: "popup" - }, - { - text: "Disable All Notifications", - value: "disabled" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "indicateSubmission", - "Submitted Assignments Checklist", - '[Reload required] Shows a checkmark, shows a strikethrough, or hides items in "Upcoming Assignments" that have been submitted. If "Show Check Mark" is selected, a checklist function will be enabled allowing you to manually mark assignments as complete.', - "check", - "select", - { - options: [ - { - text: "Show Check Mark ✔ (Enables manual checklist)", - value: "check" - }, - { - text: "Show Strikethrough (Doesn't allow manual checklist)", - value: "strikethrough" - }, - { - text: "Hide Assignment (Not recommended)", - value: "hide" - }, - { - text: "Do Nothing", - value: "disabled" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "quickAccessVisibility", - "Quick Access", - "[Reload Required to Reposition] Changes the visibility of the Quick Access panel on the homepage", - "enabled", - "select", - { - options: [ - { - text: "Top of Right Sidebar", - value: "enabled" - }, - { - text: "Between Overdue and Upcoming", - value: "belowOverdue" - }, - { - text: "Bottom of Right Sidebar", - value: "bottom" - }, - { - text: "Disabled", - value: "disabled" - } - ] - }, - value => { - setCSSVariable("quick-access-display", value === "disabled" ? "none" : "block"); - return value; - }, - function (event) { this.onload(event.target.value) }, - element => element.value - ).control, - new Setting( - "upcomingOverdueVisibility", - "Hide Upcoming and Overdue Assignments", - 'Hides the "Upcoming" and "Overdue" sidebars on the homepage', - "showAll", - "select", - { - options: [ - { - text: "Show Both", - value: "showAll" - }, - { - text: "Hide Upcoming Only", - value: "hideUpcoming" - }, - { - text: "Hide Overdue Only", - value: "hideOverdue" - }, - { - text: "Hide Both", - value: "hideAll" - } - ] - }, - value => { - setCSSVariable("overdue-assignments-display", "block"); - setCSSVariable("upcoming-assignments-display", "block"); - switch (value) { - case "hideUpcoming": - setCSSVariable("upcoming-assignments-display", "none"); - break; - case "hideOverdue": - setCSSVariable("overdue-assignments-display", "none"); - break; - case "hideAll": - setCSSVariable("upcoming-assignments-display", "none"); - setCSSVariable("overdue-assignments-display", "none"); - break; - } - return value; - }, - function (event) { this.onload(event.target.value) }, - element => element.value - ).control, - new Setting( - "courseIcons", - "Override Course Icons", - "[Refresh required to disable] Replace the course icons with the selected theme's icons", - isLAUSD() ? "enabled" : "defaultOnly", - "select", - { - options: [ - { - text: "All Icons", - value: "enabled" - }, - { - text: "Default Icons Only", - value: "defaultOnly", - }, - { - text: "Disabled", - value: "disabled" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "useDefaultIconSet", - "Use Built-In Icon Set", - `[Refresh required] Use Schoology Plus's default course icons as a fallback when a custom icon has not been specified. NOTE: these icons were meant for schools in Los Angeles Unified School District and may not work correctly for other schools.`, - isLAUSD() ? "enabled" : "disabled", - "select", - { - options: [ - { - text: "Enabled", - value: "enabled" - }, - { - text: "Disabled", - value: "disabled" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "customScales", - "Custom Grading Scales", - "[Refresh required] Uses custom grading scales (set per-course in course settings) when courses don't have one defined", - "enabled", - "select", - { - options: [ - { - text: "Enabled", - value: "enabled" - }, - { - text: "Disabled", - value: "disabled" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "orderClasses", - "Order Classes", - "[Refresh required] Changes the order of your classes on the grades and mastery pages (only works if your course names contain PER N or PERIOD N)", - "period", - "select", - { - options: [ - { - text: "By Period", - value: "period" - }, - { - text: "Alphabetically", - value: "alpha" - } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "overrideUserStyles", - "Override Styled Text", - "Override styled text in homefeed posts and discussion responses when using modern themes. WARNING: This guarantees text is readable on dark theme, but removes colors and other styling that may be important. You can always use the Toggle Theme button on the navigation bar to temporarily disble your theme.", - "true", - "select", - { - options: [ - { - text: "Enabled", - value: "true" - }, - { - text: "Disabled", - value: "false" - } - ] - }, - value => { - document.documentElement.setAttribute("style-override", value); - return value; - }, - function (event) { this.onload(event.target.value) }, - element => element.value - ).control, - new Setting( - "archivedCoursesButton", - "Archived Courses Button", - 'Adds a link to see past/archived courses in the courses dropdown', - "show", - "select", - { - options: [ - { - text: "Show", - value: "show" - }, - { - text: "Hide", - value: "hide" + createElement("div", ["splus-modal-contents", "splus-settings-tabs"], {}, [ + createElement("ul", [], {}, [ + createElement("li", [], {}, [createElement("a", [], {href: "#splus-settings-section-appearance", textContent: "Appearance"})]), + createElement("li", [], {}, [createElement("a", [], {href: "#splus-settings-section-sidebar", textContent: "Homepage/Sidebar"})]), + createElement("li", [], {}, [createElement("a", [], {href: "#splus-settings-section-grades", textContent: "Grades"})]), + createElement("li", [], {}, [createElement("a", [], {href: "#splus-settings-section-utilities", textContent: "Utilities"})]), + ]), + createElement("div", [], {id: "splus-settings-section-appearance"}, [ + new Setting( + "themeEditor", + "Theme Editor", + "Click to open the theme editor to create, edit, or select a theme", + "Theme Editor", + "button", + {}, + value => "Theme Editor", + event => location.href = chrome.runtime.getURL("/theme-editor.html") + ).control, + new Setting( + "theme", + "Theme", + "Change the theme of Schoology Plus", + "Schoology Plus", + "select", + { + options: [ + ...__defaultThemes.filter( + t => LAUSD_THEMES.includes(t.name) ? isLAUSD() : true + ).map(t => {return {text: t.name, value: t.name}}), + ...(__storage.themes || []).map( + t => {return {text: t.name, value: t.name}} + ) + ] + }, + value => { + tempTheme = undefined; + Theme.apply(Theme.active); + return value; + }, + event => { + tempTheme = event.target.value; + Theme.apply(Theme.byName(event.target.value)); + }, + element => element.value + ).control, + new Setting( + "courseIcons", + "Override Course Icons", + "[Refresh required to disable] Replace the course icons with the selected theme's icons", + isLAUSD() ? "enabled" : "defaultOnly", + "select", + { + options: [ + { + text: "All Icons", + value: "enabled" + }, + { + text: "Default Icons Only", + value: "defaultOnly", + }, + { + text: "Disabled", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "useDefaultIconSet", + "Use Built-In Icon Set", + `[Refresh required] Use Schoology Plus's default course icons as a fallback when a custom icon has not been specified. NOTE: these icons were meant for schools in Los Angeles Unified School District and may not work correctly for other schools.`, + isLAUSD() ? "enabled" : "disabled", + "select", + { + options: [ + { + text: "Enabled", + value: "enabled" + }, + { + text: "Disabled", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "overrideUserStyles", + "Override Styled Text", + "Override styled text in homefeed posts and discussion responses when using modern themes. WARNING: This guarantees text is readable on dark theme, but removes colors and other styling that may be important. You can always use the Toggle Theme button on the navigation bar to temporarily disble your theme.", + "true", + "select", + { + options: [ + { + text: "Enabled", + value: "true" + }, + { + text: "Disabled", + value: "false" + } + ] + }, + value => { + document.documentElement.setAttribute("style-override", value); + return value; + }, + function (event) { this.onload(event.target.value) }, + element => element.value + ).control, + new Setting( + "archivedCoursesButton", + "Archived Courses Button", + 'Adds a link to see past/archived courses in the courses dropdown', + "show", + "select", + { + options: [ + { + text: "Show", + value: "show" + }, + { + text: "Hide", + value: "hide" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "helpCenterFAB", + "Schoology Help Button", + "Controls the visibility of the S button in the bottom right that shows the Schoology Guide Center", + "hidden", + "select", + { + options: [ + { + text: "Show", + value: "visible" + }, + { + text: "Hide", + value: "hidden" + } + ] + }, + value => { + setCSSVariable("help-center-fab-visibility", value); + return value; + }, + function (event) { this.onload(event.target.value) }, + element => element.value + ).control, + ]), + createElement("div", [], {id: "splus-settings-section-sidebar"}, [ + new Setting( + "indicateSubmission", + "Submitted Assignments Checklist", + '[Reload required] Shows a checkmark, shows a strikethrough, or hides items in "Upcoming Assignments" that have been submitted. If "Show Check Mark" is selected, a checklist function will be enabled allowing you to manually mark assignments as complete.', + "check", + "select", + { + options: [ + { + text: "Show Check Mark ✔ (Enables manual checklist)", + value: "check" + }, + { + text: "Show Strikethrough (Doesn't allow manual checklist)", + value: "strikethrough" + }, + { + text: "Hide Assignment (Not recommended)", + value: "hide" + }, + { + text: "Do Nothing", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "toDoIconVisibility", + '"Overdue" and "Due Tomorrow" Icon Visibility', + 'Controls the visibility of the "Overdue" exclamation point icon and the "Due Tomorrow" clock icon in the Upcoming and Overdue lists on the sidebar of the homepage', + "visible", + "select", + { + options: [ + { + text: "Show Icons", + value: "visible" + }, + { + text: "Hide Icons", + value: "hidden" + } + ] + }, + value => { + setCSSVariable("to-do-list-icons-display", "block"); + switch (value) { + case "hidden": + setCSSVariable("to-do-list-icons-display", "none"); + break; } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "weightedGradebookIndicator", - "Weighted Gradebook Indicator", - "Adds an indicator next to gradebooks which are weighted", - "enabled", - "select", - { - options: [ - { - text: "Show", - value: "enabled" - }, - { - text: "Hide", - value: "disabled" + return value; + }, + function (event) { this.onload(event.target.value) }, + element => element.value + ).control, + new Setting( + "sidebarSectionOrder", + "Customize Sidebar", + "", + { + include: [], + exclude: [] + }, + "custom", + { + element: createElement("div", [], {}, [ + createElement("p", [], {style: {fontWeight: "normal"}, textContent: "Drag items between the sections to control which sections of the sidebar are visible and the order in which they are shown."}), + createElement("div", ["sortable-container"], {}, [ + createElement("div", ["sortable-list"], {}, [ + createElement("h3", ["splus-underline-heading"], {textContent: "Sections to Hide"}), + createElement("ul", ["sidebar-sortable", "splus-modern-border-radius", "splus-modern-padding"], {id: "sidebar-excluded-sortable"}) + ]), + createElement("div", ["sortable-list"], {}, [ + createElement("h3", ["splus-underline-heading"], {textContent: "Sections to Show"}), + createElement("ul", ["sidebar-sortable", "splus-modern-border-radius", "splus-modern-padding"], {id: "sidebar-included-sortable"}) + ]), + ]) + ]), + }, + function (value, element) { + let includeList = element.querySelector("#sidebar-included-sortable"); + let excludeList = element.querySelector("#sidebar-excluded-sortable"); + + includeList.innerHTML = ""; + excludeList.innerHTML = ""; + + if (!value || !value.include || !value.exclude) { + value = {include: [], exclude: []}; } - ] - }, - value => { - setCSSVariable("weighted-gradebook-indicator-display", value == "enabled" ? "inline" : "none") - return value; - }, - function (event) { this.onload(event.target.value) }, - element => element.value - ).control, - new Setting( - "broadcasts", - "Announcement Notifications", - "Displays news feed posts for announcements sent to all Schoology Plus users", - "enabled", - "select", - { - options: [ - { - text: "Enable Announcements", - value: "enabled" - }, - { - text: "Disable Announcements", - value: "disabled" + + for (let section of value.include) { + includeList.appendChild(createElement("p", ["sortable-item", "splus-modern-border-radius", "splus-modern-padding"], {textContent: section})) } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "autoBypassLinkRedirects", - "Automatically Bypass Link Redirects", - "Automatically skip the external link redirection page, clicking 'Continue' by default", - "enabled", - "select", - { - options: [ - { - text: "Enabled", - value: "enabled" - }, - { - text: "Disabled", - value: "disabled" + + for (let section of value.exclude) { + excludeList.appendChild(createElement("p", ["sortable-item", "splus-modern-border-radius", "splus-modern-padding"], {textContent: section})) } - ] - }, - value => value, - undefined, - element => element.value - ).control, - new Setting( - "helpCenterFAB", - "Schoology Help Button", - "Controls the visibility of the S button in the bottom right that shows the Schoology Guide Center", - "hidden", - "select", - { - options: [ - { - text: "Show", - value: "visible" - }, - { - text: "Hide", - value: "hidden" + + for (let section of SIDEBAR_SECTIONS) { + if (!value.include.includes(section.name) && !value.exclude.includes(section.name)) { + includeList.appendChild(createElement("p", ["sortable-item", "splus-modern-border-radius", "splus-modern-padding"], {textContent: section.name})) + } } - ] - }, - value => { - setCSSVariable("help-center-fab-visibility", value); - return value; - }, - function (event) { this.onload(event.target.value) }, - element => element.value - ).control, - new Setting( - "sessionCookiePersist", - "Stay Logged In", - "[Logout/login required] Stay logged in to Schoology when you restart your browser", - "disabled", - "select", - { - options: [ - { - text: "Enabled", - value: "enabled" - }, - { - text: "Disabled", - value: "disabled" + }, + function (event) { console.log(event); }, + element => { + let includeList = element.querySelector("#sidebar-included-sortable"); + let excludeList = element.querySelector("#sidebar-excluded-sortable"); + + return { + include: Array.from(includeList.children).map(e => e.textContent), + exclude: Array.from(excludeList.children).map(e => e.textContent) } - ] - }, - value => value, - undefined, - element => element.value - ).control, - createElement("div", ["setting-entry"], {}, [ - createElement("h2", ["setting-title"], {}, [ - createElement("a", [], { href: "#", textContent: "Change Schoology Account Access", onclick: () => {location.pathname = "/api";}, style: { fontSize: "" } }) - ]), - createElement("p", ["setting-description"], { textContent: "Grant Schoology Plus access to your Schoology API Key so many features can function, or revoke that access." }) + }, + function () { + $(".sidebar-sortable").sortable({ + connectWith: ".sidebar-sortable", + stop: () => Setting.onModify(this.getElement()) + }).disableSelection(); + } + ).control, + ]), + createElement("div", [], {id: "splus-settings-section-grades"}, [ + new Setting( + "customScales", + "Custom Grading Scales", + "[Refresh required] Uses custom grading scales (set per-course in course settings) when courses don't have one defined", + "enabled", + "select", + { + options: [ + { + text: "Enabled", + value: "enabled" + }, + { + text: "Disabled", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "orderClasses", + "Order Classes", + "[Refresh required] Changes the order of your classes on the grades and mastery pages (only works if your course names contain PER N or PERIOD N)", + "period", + "select", + { + options: [ + { + text: "By Period", + value: "period" + }, + { + text: "Alphabetically", + value: "alpha" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "weightedGradebookIndicator", + "Weighted Gradebook Indicator", + "Adds an indicator next to gradebooks which are weighted", + "enabled", + "select", + { + options: [ + { + text: "Show", + value: "enabled" + }, + { + text: "Hide", + value: "disabled" + } + ] + }, + value => { + setCSSVariable("weighted-gradebook-indicator-display", value == "enabled" ? "inline" : "none") + return value; + }, + function (event) { this.onload(event.target.value) }, + element => element.value + ).control, ]), - getBrowser() !== "Firefox" ? createElement("div", ["setting-entry"], {}, [ - createElement("h2", ["setting-title"], {}, [ - createElement("a", [], { href: "#", textContent: "Anonymous Usage Statistics", onclick: () => openModal("analytics-modal"), style: { fontSize: "" } }) + createElement("div", [], {id: "splus-settings-section-utilities"}, [ + new Setting( + "notifications", + "Desktop Notifications", + "Displays desktop notifications and a number badge on the extension button when new grades are entered", + "enabled", + "select", + { + options: [ + { + text: "Enable All Notifications", + value: "enabled" + }, + { + text: "Number Badge Only (No Pop-Ups)", + value: "badge" + }, + { + text: "Pop-Ups Only (No Badge)", + value: "popup" + }, + { + text: "Disable All Notifications", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "broadcasts", + "Announcement Notifications", + "Displays news feed posts for announcements sent to all Schoology Plus users", + "enabled", + "select", + { + options: [ + { + text: "Enable Announcements", + value: "enabled" + }, + { + text: "Disable Announcements", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "autoBypassLinkRedirects", + "Automatically Bypass Link Redirects", + "Automatically skip the external link redirection page, clicking 'Continue' by default", + "enabled", + "select", + { + options: [ + { + text: "Enabled", + value: "enabled" + }, + { + text: "Disabled", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + new Setting( + "sessionCookiePersist", + "Stay Logged In", + "[Logout/login required] Stay logged in to Schoology when you restart your browser", + "disabled", + "select", + { + options: [ + { + text: "Enabled", + value: "enabled" + }, + { + text: "Disabled", + value: "disabled" + } + ] + }, + value => value, + undefined, + element => element.value + ).control, + createElement("div", ["setting-entry"], {}, [ + createElement("h2", ["setting-title"], {}, [ + createElement("a", [], { href: "#", textContent: "Change Schoology Account Access", onclick: () => {location.pathname = "/api";}, style: { fontSize: "" } }) + ]), + createElement("p", ["setting-description"], { textContent: "Grant Schoology Plus access to your Schoology API Key so many features can function, or revoke that access." }) ]), - createElement("p", ["setting-description"], { textContent: "[Reload required] Allow Schoology Plus to collect anonymous information about how you use the extension. We don't collect any personal information per our privacy policy." }) - ]) : noControl, - + getBrowser() !== "Firefox" ? createElement("div", ["setting-entry"], {}, [ + createElement("h2", ["setting-title"], {}, [ + createElement("a", [], { href: "#", textContent: "Anonymous Usage Statistics", onclick: () => openModal("analytics-modal"), style: { fontSize: "" } }) + ]), + createElement("p", ["setting-description"], { textContent: "[Reload required] Allow Schoology Plus to collect anonymous information about how you use the extension. We don't collect any personal information per our privacy policy." }) + ]) : noControl, + ]), ]), createElement("div", ["settings-buttons-wrapper"], undefined, [ createButton("save-settings", "Save Settings", () => Setting.saveModified()), @@ -891,13 +975,15 @@ let __settings = {}; * - First argument is the HTML element containing the setting value set by the user * - Must return the value to be saved to extension settings * - Will only be called if user saves settings and setting was modified + * @param {function():any} onshown Function called when the setting element is shown on screen */ -function Setting(name, friendlyName, description, defaultValue, type, options, onload, onmodify, onsave) { +function Setting(name, friendlyName, description, defaultValue, type, options, onload, onmodify, onsave, onshown) { this.name = name; this.getElement = () => document.getElementById(`setting-input-${this.name}`); this.onmodify = onmodify; this.onsave = onsave; this.onload = onload; + this.onshown = onshown; this.modified = false; this.default = defaultValue; /** @@ -926,6 +1012,9 @@ function Setting(name, friendlyName, description, defaultValue, type, options, o title.appendChild(selectElement); selectElement.onchange = Setting.onModify; break; + case "custom": + title.appendChild(options.element); + break; } setting.appendChild(title); @@ -939,7 +1028,7 @@ function Setting(name, friendlyName, description, defaultValue, type, options, o } if (onload) { - title.firstElementChild.value = onload(__storage[name]) || this.default; + title.firstElementChild.value = this.onload(__storage[name], title.firstElementChild) || this.default; } else { title.firstElementChild.value = __storage[name] || this.default; } @@ -1099,6 +1188,14 @@ Setting.onModify = function (event) { } } +Setting.onShown = function () { + for (let setting in __settings) { + if (__settings[setting].onshown) { + __settings[setting].onshown(); + } + } +} + /** * @returns {boolean} `true` if any setting has been modified */ diff --git a/js/theme-editor.js b/js/theme-editor.js index df1a4f5..e217764 100644 --- a/js/theme-editor.js +++ b/js/theme-editor.js @@ -5,8 +5,6 @@ const lausd2019ImageUrl = chrome.runtime.getURL("/imgs/lausd-2019.png"); const lausd2022ImageUrl = chrome.runtime.getURL("/imgs/lausd-2022.png"); const CURRENT_VERSION = SchoologyTheme.CURRENT_VERSION; const placeholderUrl = "https://via.placeholder.com/200x50?text=School+Logo"; -const LAUSD_THEMES = ["LAUSD Orange", "LAUSD Dark Blue", "LAUSD 2019"]; -const CLASSIC_THEMES = ["Schoology Plus", "Rainbow"] var defaultDomain = "app.schoology.com"; @@ -1709,11 +1707,11 @@ $(document).ready(function () { } for (let t of __defaultThemes) { + defaultThemes.push(t.name); if (!isLAUSD() && LAUSD_THEMES.includes(t.name)) { continue; } allThemes[t.name] = t; - defaultThemes.push(t.name); } chrome.storage.sync.get(["theme", "themes"], s => { diff --git a/manifest.json b/manifest.json index 2d4769e..4bb7ede 100644 --- a/manifest.json +++ b/manifest.json @@ -10,7 +10,7 @@ "update_url": "https://schoologypl.us/firefox_updates.json" } }, - "version": "7.7.3", + "version": "7.8", "icons": { "128": "imgs/icon@128.png", "64": "imgs/icon@64.png", @@ -118,10 +118,12 @@ ], "css": [ "lib/css/contextmenu.css", - "lib/css/iziToast.min.css" + "lib/css/iziToast.min.css", + "/lib/css/jquery-ui.min.css" ], "js": [ "lib/js/jquery-3.3.1.min.js", + "/lib/js/jquery-ui.min.js", "lib/js/contextmenu.js", "lib/js/iziToast.min.js", "js/version-specific.js", diff --git a/scss/modern/all.scss b/scss/modern/all.scss index 9e65c9e..d7e2abd 100644 --- a/scss/modern/all.scss +++ b/scss/modern/all.scss @@ -3799,4 +3799,12 @@ variable-intellisense { #grade-comment-field { background-color: var(--primary); } + + .splus-modern-border-radius { + border-radius: var(--border-radius); + } + + .splus-modern-padding { + padding: var(--padding); + } } diff --git a/theme-editor.html b/theme-editor.html index fcf53f6..ecbe472 100644 --- a/theme-editor.html +++ b/theme-editor.html @@ -348,7 +348,7 @@