// ==UserScript==
// @name Edio Hotkeys
// @namespace http://tampermonkey.net/
// @version Alpha-v3
// @description Adds shortcuts to get across the site faster
// @author Crowned Studios
// @license CC BY-NC
// @match https://www.myedio.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_cookieSet
// @grant GM_cookieDelete
// ==/UserScript==
/*
_ _ _ _ _____
| | | | | | | | _| __ \
| |__| | ___ | |_| | _____ _ _ ___ (_) | | |
| __ |/ _ \| __| |/ / _ \ | | / __| | | | |
| | | | (_) | |_| < __/ |_| \__ \ _| |__| |
|_| |_|\___/ \__|_|\_\___|\__, |___/ (_)_____/
__/ |
|___/
*/
// === User Configurable Settings ===
// Set this to true to enable console logging, or false to disable it.
const isLoggingEnabled = true;
// === End Of Configurable Settings ===
(function() {
'use strict';
function logMessage(message) {
if (isLoggingEnabled) {
console.log(message);
}
}
function saveLinkIfMatches() {
const link = window.location.href;
const matchPatterns = [
"https://www.myedio.com/learning/courses/",
"/lessons/",
"/summary/"
];
if (matchPatterns.every(pattern => link.includes(pattern))) {
try {
GM_setValue('siteLink', link);
logMessage(`Link saved using GM_setValue: ${link}`);
} catch (e) {
logMessage("Error saving with GM_setValue, falling back to cookies...");
showErrorPopup(101, e.message);
document.cookie = `siteLink=${link}; path=/; expires=${new Date(Date.now() + 86400000).toUTCString()}`;
logMessage("Link saved to cookie: " + link);
}
} else {
logMessage("Link does not match the pattern, not saved.");
}
}
function getSavedLink() {
return new Promise((resolve, reject) => {
const gmLink = GM_getValue('siteLink', null);
if (gmLink) {
resolve(gmLink);
} else {
const cookieMatch = document.cookie.match(/siteLink=([^;]*)/);
if (cookieMatch) {
resolve(cookieMatch[1]);
} else {
showErrorPopup(102, "No site link found, or It was never saved.");
reject("No site link found, or It was never saved.");
}
}
});
}
async function handleHotkeys(event) {
if (event.altKey) {
const key = event.key.toLowerCase();
switch (key) {
case 'r':
event.preventDefault();
try {
const savedLink = await getSavedLink();
logMessage(`Redirecting to saved link: ${savedLink}`);
window.location.href = savedLink;
} catch (e) {
logMessage(e);
showErrorPopup(103, "Error redirecting to saved link.");
}
break;
case 'c':
event.preventDefault();
window.location.href = "https://www.myedio.com/calendar/day/";
break;
case 'q':
event.preventDefault();
window.location.href = "https://www.myedio.com/login/?sessionExpired=true";
break;
case 'd':
event.preventDefault();
window.location.href = "https://www.myedio.com/dashboard/";
break;
case 'l':
event.preventDefault();
alert("Test successful!");
break;
default:
handleCustomHotkeys(key);
break;
}
}
}
function handleCustomHotkeys(key) {
const customHotkeys = GM_getValue('customHotkeys', {});
if (customHotkeys[key]) {
window.location.href = customHotkeys[key];
}
}
function showErrorPopup(errorNumber, errorMessage) {
if (document.querySelector('.error-overlay')) return;
const overlay = document.createElement('div');
overlay.classList.add('error-overlay');
const overlayContent = document.createElement('div');
overlayContent.classList.add('error-overlay-content');
overlayContent.innerHTML = `
<h2>Error ${errorNumber}</h2>
<p>${errorMessage}</p>
`;
const closeButton = document.createElement('button');
closeButton.innerHTML = 'Close';
closeButton.addEventListener('click', () => {
document.body.removeChild(overlay);
document.body.removeChild(overlayBackground);
document.body.style.overflow = '';
document.body.style.userSelect = '';
});
overlayContent.appendChild(closeButton);
overlay.appendChild(overlayContent);
document.body.appendChild(overlay);
const overlayBackground = document.createElement('div');
overlayBackground.classList.add('error-overlay-background');
document.body.appendChild(overlayBackground);
document.body.style.overflow = 'hidden';
document.body.style.userSelect = 'none';
}
function addHotkeysElement() {
if (document.querySelector('.c-navigation__item.hotkeys-item')) return;
const newElement = document.createElement('li');
newElement.classList.add('c-navigation__item', 'hotkeys-item');
newElement.innerHTML = `
<a class="" target="" title="Hotkeys">
<span>Hotkeys</span>
</a>
`;
const navigationElement = document.querySelector('.c-navigation');
if (navigationElement) navigationElement.appendChild(newElement);
newElement.addEventListener('click', showHotkeysOverlay);
observeSidebarToggle(newElement);
}
function observeSidebarToggle(hotkeysElement) {
const toggleButton = document.querySelector('.c-button.-icon.c-sidebar__toggle');
if (toggleButton) {
const observer = new MutationObserver(() => {
const isExpanded = toggleButton.getAttribute('aria-expanded') === 'true';
const span = hotkeysElement.querySelector('span');
span.textContent = isExpanded ? 'Hotkeys' : 'HK';
});
observer.observe(toggleButton, { attributes: true, attributeFilter: ['aria-expanded'] });
}
}
function showHotkeysOverlay() {
const overlay = document.createElement('div');
overlay.classList.add('hotkeys-overlay');
const overlayContent = document.createElement('div');
overlayContent.classList.add('hotkeys-overlay-content');
const userNameElement = document.querySelector('.c-avatar__name');
let userName = userNameElement ? userNameElement.textContent.trim() : "User";
function toTitleCase(str) {
return str.replace(/\b\w/g, char => char.toUpperCase());
}
userName = toTitleCase(userName);
overlayContent.innerHTML = `
<h2>List of Hotkeys</h2>
<p><strong>Logout:</strong> Alt + Q</p>
<p><strong>Calendar:</strong> Alt + C</p>
<p><strong>Dashboard:</strong> Alt + D</p>
<p><strong>Return to Course Link:</strong> Alt + R</p>
<p><strong>Test Key:</strong> Alt + L</p>
<p><strong>Custom Hotkeys:</strong></p>
<ul id="custom-hotkeys-list"></ul>
<p>Hello ${userName}! You are running the Alpha version of <strong>Edio Hotkeys</strong>.
There is still lots of development needed for this script, including one known error,
but please enjoy what you currently have!</p>
<button id="add-custom-hotkey">Add Custom Hotkey (Experimental)</button>
`;
const closeButton = document.createElement('button');
closeButton.innerHTML = 'Close';
closeButton.addEventListener('click', () => {
document.body.removeChild(overlay);
document.body.removeChild(overlayBackground);
document.body.style.overflow = '';
document.body.style.userSelect = '';
});
overlayContent.appendChild(closeButton);
overlay.appendChild(overlayContent);
document.body.appendChild(overlay);
const overlayBackground = document.createElement('div');
overlayBackground.classList.add('hotkeys-overlay-background');
document.body.appendChild(overlayBackground);
document.body.style.overflow = 'hidden';
document.body.style.userSelect = 'none';
displayCustomHotkeys();
document.getElementById('add-custom-hotkey').addEventListener('click', addCustomHotkey);
}
function displayCustomHotkeys() {
const customHotkeys = GM_getValue('customHotkeys', {});
const customHotkeysList = document.getElementById('custom-hotkeys-list');
customHotkeysList.innerHTML = '';
for (const [key, url] of Object.entries(customHotkeys)) {
const li = document.createElement('li');
li.textContent = `Alt + ${key.toUpperCase()}: ${url}`;
customHotkeysList.appendChild(li);
}
}
function addCustomHotkey() {
const key = prompt("Enter the key for the custom hotkey (e.g., 'n' for Alt+N):");
const url = prompt("Enter the URL to be associated with this hotkey:");
if (key && url) {
const customHotkeys = GM_getValue('customHotkeys', {});
customHotkeys[key.toLowerCase()] = url;
GM_setValue('customHotkeys', customHotkeys);
displayCustomHotkeys();
}
}
const style = document.createElement('style');
style.innerHTML = `
.c-navigation__item.hotkeys-item a {
display: flex;
align-items: center;
color: #333;
font-size: 16px;
text-decoration: none;
padding: 10px;
border-radius: 4px;
transition: background-color 0.2s ease, transform 0.2s ease;
cursor: pointer;
}
.c-navigation__item.hotkeys-item a:hover {
background-color: #f4f4f4;
transform: translateX(5px);
cursor: pointer;
}
.hotkeys-overlay-background {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
pointer-events: none;
}
.hotkeys-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1001;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
animation: fadeIn 0.3s forwards;
}
.hotkeys-overlay-content {
background: #ffffff;
padding: 30px 25px;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
max-width: 600px;
text-align: center;
}
.hotkeys-overlay-content h2 {
margin-bottom: 20px;
font-size: 24px;
}
.hotkeys-overlay-content p {
margin-bottom: 15px;
font-size: 16px;
}
.hotkeys-overlay-content ul {
list-style: none;
padding: 0;
}
.hotkeys-overlay-content ul li {
margin-bottom: 5px;
font-size: 16px;
}
.hotkeys-overlay-content button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.hotkeys-overlay-content button:hover {
background-color: #0056b3;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.error-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1001;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.8);
}
.error-overlay-content {
background: #ffebee;
padding: 20px;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
max-width: 500px;
text-align: center;
}
.error-overlay-content h2 {
margin-bottom: 15px;
font-size: 20px;
color: #d32f2f;
}
.error-overlay-content p {
margin-bottom: 15px;
font-size: 16px;
color: #333;
}
.error-overlay-content button {
background-color: #d32f2f;
color: white;
border: none;
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
border-radius: 4px;
margin-top: 10px;
}
.error-overlay-content button:hover {
background-color: #b71c1c;
}
`;
document.head.appendChild(style);
saveLinkIfMatches();
window.addEventListener('keydown', handleHotkeys);
setInterval(() => {
addHotkeysElement();
}, 10);
})();