// ==UserScript== // @name MyGCC plus // @namespace https://github.com/jakethurman/mygcc-plus // @version 1.50 // @description mygcc-plus // @downloadURL https://github.com/jakethurman/mygcc-plus/raw/master/mygcc-plus.user.js // @author Jake Thurman and Ian Spryn // @match https://my.gcc.edu/ICS** // @match https://my.gcc.edu/ics** // ==/UserScript== /* To use this script 1. Install the TamperMokey plugin for google chrome (or GreeseMonkey for Firefox) 2. Navigate to: https://github.com/JakeThurman/mygcc-plus/raw/master/mygcc-plus.user.js To update: - Navigate to the given url, or use the tampermonkey "Check for userscript updates" feature; Customizing: - Settings are at the bottom of the page, in the footer of mygcc Features: 1. Courses is always expanded 2. Auto logs you in (OPTIONAL) 3. Keeps you logged in (usually) 4. Looks better (OPTIONAL) 5. Courses links go to Coursework page (OPTIONAL) 6. CTRL+, to jump to courses 7. The "Low Hanging Fruit" bugs of MyGCC are fixed a. examples: page jumps down to the bottom when you laod the page portlets on sign-up page are no longer cut off */ (function () { 'use strict'; var util = (function () { function getMostCommonEl(arr) { return arr.sort((a, b) => arr.filter(v => v === a).length - arr.filter(v => v === b).length ).pop(); } // Source taken from _.once function memoize(func) { var ran = false, memo; return function () { if (ran) return memo; ran = true; memo = func.apply(this, arguments); func = null; return memo; }; } function urlCombine(a, b) { a = a.endsWith("/") ? a.slice(0, -1) : a; return a + b; } function onError(e) { console.error(e); } function getStringAcronym(str) { return (str.match(/\b(\w)/g) || []).join(""); } return { getMostCommonEl, memoize, urlCombine, onError, getStringAcronym, }; })(); var getOptionContainer = util.memoize(function () { return $("
") .appendTo($("#GRCbottombar")) .css({ "padding-left": "20%" }); }); function addTextAndIcon(element, text, icon) { if (text !== null) { if (text instanceof Array) { for (let str of text) { var textElement = document.createElement('p'); textElement.textContent = str; element.appendChild(textElement) } } else { var textElement = document.createElement('p'); textElement.textContent = text; element.appendChild(textElement) } } if (icon !== null) { var iconElement = document.createElement('i'); iconElement.style.fontSize = "40px"; iconElement.classList = "material-icons" iconElement.textContent = icon; element.appendChild(iconElement); } } function createButtonTable(inputs) { let elePerLine = 2; var table = document.createElement('table'); table.classList = "upload-table-buttons" table.style.margin = "0 auto"; //center it var tr, i = 0; //I spent an hour trying to get this to work with a for-loop //Apparently, tr.appendChild(input) removes the element from the inputs array. WHYYYY while (inputs.length > 0) { let input = inputs[0] if (i % elePerLine == 0) { tr = document.createElement('tr'); table.appendChild(tr) } input.classList.add('my-gcc-plus-button') if (input.id.includes('btnEdit')) { input.classList.add("my-gcc-plus-button"); input.classList.add("my-gcc-plus-button-default"); input.src = "https://fonts.gstatic.com/s/i/materialicons/edit/v1/24px.svg"; } else if (input.id.includes('btnDelete')) { input.classList.add("my-gcc-plus-button-red"); input.src = "https://fonts.gstatic.com/s/i/materialicons/delete_outline/v1/24px.svg"; } var td = document.createElement('td') td.appendChild(input); tr.appendChild(td); i++; } return table; } function convertToButton(element, text, icon, color = "default") { if (color) { element.classList.add('my-gcc-plus-button'); element.classList.add('my-gcc-plus-button-' + color); } else { element.classList.add('my-gcc-plus-button-inactive'); } element.innerHTML = null addTextAndIcon(element, text, icon) return element; } function createButton(type, id = null, text = null, icon = null, color = "default", onclick = null, href = null, inputs = null) { var element = document.createElement(type) if (id != null) element.id = id; if (color) { element.classList = "my-gcc-plus-button"; element.classList.add("my-gcc-plus-button-" + color); } else { element.classList = "my-gcc-plus-button-inactive"; } addTextAndIcon(element, text, icon) if (type == 'button') { element.setAttribute("type", "button"); } else if (type == 'a') { element.target = "_blank" //open in new tab } if (onclick != null) element.setAttribute('onclick', onclick); if (href != null) { element.href = href; } if (inputs !== null) { var container = document.createElement('div'); element.style.marginBottom = "20px"; container.appendChild(element); container.appendChild(createButtonTable(inputs)); return container; } return element; } function updateUploadAndSubmitButtons() { /* Upload button */ var uploadButton = document.getElementById('pg0_V_UploadAssignmentDetails__hypUploadFile'); if (uploadButton) { uploadButton.classList.add('my-gcc-plus-button') uploadButton.classList.add('my-gcc-plus-button-default') //allign the upload button and submit button uploadButton.parentNode.style.margin = "0 auto 30px auto" uploadButton.parentNode.style.display = "flex" uploadButton.parentNode.style.alignItems = "center" uploadButton.parentNode.parentNode.style.display = "flex" } /* Submit button */ var submitButton = document.getElementById('pg0_V_UploadAssignmentDetails__lbtnTurnIn'); if (submitButton) { submitButton.classList.add('my-gcc-plus-button') //Custom CSS for an assignment page with an overdue submission. if (document.querySelector('.lateAssignment') !== null) { submitButton.classList.add("my-gcc-plus-button-red") } else { submitButton.classList.add('my-gcc-plus-button-blue') } //add click listener that will remove the edit and delete buttons below each uploaded file $("#pg0_V_UploadAssignmentDetails__lbtnTurnIn").click(function () { $(".upload-table-buttons").remove() }) } } try { var t_jq0 = performance.now(); // Local storage keys for mygcc username and password var local_storage_username_key = "mygccplus_un", local_storage_password_key = "mygccplus_pw", local_storage_restyle_key = "mygccplus_style", local_storage_goto_coursework_key = "mygccplus_goto_coursework", ss_just_logged_in = "mygccplus_justloggedin", ss_last_page = "mygccplus_lastpage", ss_just_logged_in_2 = "mygccplus_justloggedin_noloop", ss_should_auto_log_in = "mygccplus_auto_log_in"; // Fix redirect on login var justLoggedIn = JSON.parse(sessionStorage[ss_just_logged_in] || "false"); var justLoggedIn2 = JSON.parse(sessionStorage[ss_just_logged_in_2] || "false"); //default autoLogIn to true if localStorage is null var autoLogIn = JSON.parse(localStorage.getItem(ss_should_auto_log_in)) != null ? JSON.parse(localStorage.getItem(ss_should_auto_log_in) || "false") : true; var lastPage = sessionStorage[ss_last_page]; if (autoLogIn) { if (justLoggedIn && !justLoggedIn2 && (!document.refferer || document.referrer.indexOf("my.gcc.edu") >= 0)) { // Clear loggin flag so we don't infinity loop sessionStorage.setItem(ss_just_logged_in, JSON.stringify(false)); sessionStorage.setItem(ss_just_logged_in_2, JSON.stringify(true)); try { //Go back if we can! if (lastPage) { history.back(); } } catch (e) { util.onError(e); } } } // Setup var interval = 1000 * 60; // After a minute setTimeout(function callback() { try { // Load the home page every minute // just to trick MyGCC into not logging us out! $.get("/ICS/", function () { setTimeout(callback, interval); }); } catch (e) { util.onError(e); } // ajaxUtil.callAsmxWebService('services/sessionkeepalive.asmx/Ping'); //come back to see if I can use this for more efficient solution? }, interval); lastPage = location.href; function saveCredentials(username, password) { try { localStorage.setItem(local_storage_username_key, username); localStorage.setItem(local_storage_password_key, password); console.log(username) console.log(password) } catch (e) { alert(e); } } if (autoLogIn) { // Capture click on submit button on invalid login page to get username and password $("#siteNavBar_welcomeBackBarLoggedOut_ButtonLogin").click(function (e) { //if true, this is a real click and not a programmatic click if (e.hasOwnProperty('originalEvent')) saveCredentials(document.getElementsByName("userName")[0].value, document.getElementsByName("password")[0].value) return true }); // If username and password are in local storage if (localStorage.getItem(local_storage_username_key) !== null && localStorage.getItem(local_storage_password_key) !== null) { // If username and password DOM inputs are in DOM if (document.getElementsByName("userName")[0] && document.getElementsByName("password")[0]) { // Set username and password inputs with username and password already in local storage document.getElementsByName("userName")[0].value = localStorage.getItem(local_storage_username_key); document.getElementsByName("password")[0].value = localStorage.getItem(local_storage_password_key); sessionStorage.setItem(ss_just_logged_in, JSON.stringify(true)); // Click the submit button $("#siteNavBar_welcomeBackBarLoggedOut_ButtonLogin").click(); } } else { // Capture click on submit button to get username and password on homepage $("#siteNavBar_welcomeBackBarLoggedOut_ButtonLogin").click(function () { saveCredentials(document.getElementsByName("userName")[0].value, document.getElementsByName("password")[0].value) return true }); } } var t_jq1 = performance.now(); var ralewayFont = document.createElement('link'); ralewayFont.href = 'https://fonts.googleapis.com/css?family=Raleway&display=swap' ralewayFont.rel = 'stylesheet'; document.head.appendChild(ralewayFont); var manjariFont = document.createElement('link'); manjariFont.href = 'https://fonts.googleapis.com/css?family=Manjari&display=swap' manjariFont.rel = 'stylesheet'; document.head.appendChild(manjariFont); var materialIcons = document.createElement('link'); materialIcons.href = 'https://fonts.googleapis.com/icon?family=Material+Icons' materialIcons.rel = 'stylesheet'; document.head.appendChild(materialIcons); //Make the courswork portlet 100% wide instead of 50% wide if ((window.location.href.indexOf("Attendance") > -1) || (window.location.href.indexOf("Course_Information") > -1) || (window.location.href.indexOf("Coursework") > -1) || (window.location.href.indexOf("Gradebook") > -1)) { $(".portlet-column").addClass("portlet-max-width"); } //Fix portlets going beyond their alotted space when your screen is too tiny $("iframe").addClass("proper-iframe-borders"); //A really nasty way to move the "Student Dining Options" and "Contact Information" because it's too big to fit on a tiny screen with 50% width. Good job GCC. Good. Job. //Don't even bother trying to understand what I did. Just trust the force, Luke. if ($(window).width() < 1750) { if (window.location.href.indexOf("Sign-Up") > -1) { $("
", { "class": "portlet-grid-modified" }) .insertAfter($(".portlet-grid")) $("
", { "class": "row-modified" }) .appendTo($(".portlet-grid-modified")) $("
", { "class": "new-portlets" }) .appendTo($(".portlet-grid-modified")) $("
", { "class": "holder" }) .appendTo($(".portlet-grid-modified")) .append($("#pg2_Universal")) .append($("#pg6_Universal")); $(".portlet-grid-modified").addClass("portlet-grid"); $(".row-modified").addClass("row"); } } // Add option in footer for styling var doStyling = addOption(local_storage_restyle_key, "Restyle Site", true); var headerSize = addMultiOption("mygcc-plus--header-height", "Header Height", [ { key: "tall", text: "Tall" }, { key: "normal", text: "Normal" }, { key: "shortest", text: "Short" } ], "shortest"); var hideAds = addOption("mygcc-plus--hide-ads", "Hide Ads (except on Home)", true); var shadowsOrBorders = addMultiOption("mygcc-plus-shadow-border", "Choose shadows or borders for UI", [ { key: "shadows", text: "Shadows" }, { key: "borders", text: "Borders" } ], "shadows"); addOption(ss_should_auto_log_in, "Automatically log you in", true, true); if (headerSize === "shortest") { $("
", { "id": "space" }).insertAfter($("#masthead")); // If we're NOT on login page, then we need can hide the header's background color // becauase it's no longer needed as a backdrop for the login fields if (!document.getElementById("siteNavBar_welcomeBackBarLoggedOut_ButtonLogin")) { $("