// ==UserScript==
// @name Edio Hotkeys
// @namespace http://tampermonkey.net/
// @version Release-1
// @icon https://raw.githubusercontent.com/MineverseTutorials/Userscripts/refs/heads/main/images/Hotkeys-Icon-big_enhanced.png
// @description Adds shortcuts to get across the site faster
// @author Unknown Hacker
// @license CC BY-NC
// @run-at document-start
// @match https://www.myedio.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_cookieSet
// @grant GM_cookieDelete
// @grant GM_info
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// @connect greasyfork.org
// ==/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';
// Setup
const GM = window.GM || {};
function showSetupMenu() {
if (GM_getValue('setupComplete', false)) return;
const validPages = [
"https://www.myedio.com/dashboard",
"https://www.myedio.com/directory/users/"
];
const currentURL = window.location.href;
const isValidPage = validPages.some(page => currentURL.includes(page));
if (!isValidPage) {
alert("You must be on the dashboard or the user directory page to start setup.");
return;
}
if (currentURL === "https://www.myedio.com/login/") return;
const setupOverlay = document.createElement('div');
setupOverlay.classList.add('setup-overlay');
setupOverlay.innerHTML = `
<div class="setup-content">
<h2>Welcome to Edio Hotkeys!</h2>
<p>Let's quickly set up your hotkeys to enhance your experience.</p>
<button id="highlight-button" class="setup-highlight">Get Started</button>
<p id="setup-instruction">Click "Get Started" to begin!</p>
</div>
`;
document.body.appendChild(setupOverlay);
blockRestrictedInteractions();
const style = document.createElement('style');
style.innerHTML = `
.setup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.75);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease;
pointer-events: auto;
}
.setup-content {
background: #fff;
padding: 25px;
border-radius: 10px;
text-align: center;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.3);
max-width: 400px;
width: 90%;
pointer-events: auto;
}
.setup-highlight {
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
transition: background-color 0.3s ease;
}
.setup-highlight:hover {
background-color: #0056b3;
}
`;
document.head.appendChild(style);
document.getElementById('highlight-button').addEventListener('click', () => {
moveOverlayToButton();
});
}
function blockRestrictedInteractions() {
const restrictedElements = document.querySelectorAll('.c-navigation__item, .c-kop-logo, .Edio_Hotkeys');
restrictedElements.forEach(element => {
element.style.pointerEvents = 'none';
});
}
function moveOverlayToButton() {
const setupOverlay = document.querySelector('.setup-overlay');
const targetButton = document.querySelector('.c-button.-icon.-text');
if (!targetButton) {
alert("Could not find the button to highlight. Please check the site.");
return;
}
setupOverlay.style.zIndex = '2';
setupOverlay.style.pointerEvents = 'none';
targetButton.style.outline = "3px solid red";
targetButton.style.outlineOffset = "5px";
const instruction = document.getElementById('setup-instruction');
instruction.textContent = "Click the highlighted button to continue.";
targetButton.addEventListener('click', () => {
targetButton.style.outline = "";
highlightProfileButton();
}, { once: true });
}
function highlightProfileButton() {
const setupOverlay = document.querySelector('.setup-overlay');
setupOverlay.style.zIndex = '2';
setupOverlay.style.pointerEvents = '';
setupOverlay.style.background = 'rgba(0, 0, 0, 0.75)';
const setupContent = document.querySelector('.setup-content');
setupContent.innerHTML = `
<h2>One More Step!</h2>
<p>We need you to click a user directory link. Please click on the highlighted link below.</p>
`;
waitForElementWithHref('/directory/users/');
}
function waitForElementWithHref(hrefSubstring) {
const interval = setInterval(() => {
const userLink = Array.from(document.querySelectorAll('a'))
.find(link => link.href.includes(hrefSubstring));
if (userLink) {
clearInterval(interval);
userLink.style.outline = "3px solid red";
userLink.style.outlineOffset = "5px";
const setupContent = document.querySelector('.setup-content');
setupContent.innerHTML = `
<h2>Click the Highlighted Link</h2>
<p>Click the highlighted link to complete the setup.</p>
`;
userLink.addEventListener('click', () => {
userLink.style.outline = "";
saveLinkAndCompleteSetup(userLink.href);
}, { once: true });
}
}, 500);
}
function saveLinkAndCompleteSetup(link) {
const userId = link.match(/\/directory\/users\/(\d+)/);
if (userId && userId[1]) {
GM_setValue('userId', userId[1]);
}
GM_setValue('setupComplete', true);
const setupOverlay = document.querySelector('.setup-overlay');
if (setupOverlay) {
setupOverlay.remove();
}
showCompletionMessage();
}
function showCompletionMessage() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList" && document.querySelector('.dashboard-content')) {
observer.disconnect();
if (!GM_getValue('successMessageShown', false)) {
const setupOverlay = document.createElement('div');
setupOverlay.classList.add('setup-overlay');
setupOverlay.innerHTML = `
<div class="setup-content">
<h2>Setup Complete!</h2>
<p>You have successfully set up your Edio Hotkeys!</p>
<button id="ok-button" class="setup-highlight">OK</button>
</div>
`;
document.body.appendChild(setupOverlay);
const style = document.createElement('style');
style.innerHTML = `
.setup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.75);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease;
}
.setup-content {
background: #fff;
padding: 25px;
border-radius: 10px;
text-align: center;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.3);
max-width: 400px;
width: 90%;
}
.setup-highlight {
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
transition: background-color 0.3s ease;
}
.setup-highlight:hover {
background-color: #0056b3;
}
`;
document.head.appendChild(style);
document.getElementById('ok-button').addEventListener('click', () => {
GM_setValue('successMessageShown', true);
window.location.href = "https://www.myedio.com/dashboard";
});
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
window.addEventListener('load', showSetupMenu);
// End of Setup
function logMessage(message) {
if (isLoggingEnabled) {
console.log(message);
}
}
function observeUrlChanges() {
let previousUrl = window.location.href;
const observer = new MutationObserver(() => {
if (window.location.href !== previousUrl) {
previousUrl = window.location.href;
logMessage(`URL changed to: ${previousUrl}`);
saveLinkIfMatches();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
window.addEventListener('load', () => {
observeUrlChanges();
saveLinkIfMatches();
});
function saveLinkIfMatches() {
const link = window.location.href;
const matchPatterns = [
"https://www.myedio.com/learning/courses/",
"/lessons/",
"/summary/",
"/take/",
"/days/",
"/variants/"
];
const matches = matchPatterns.some(pattern => link.includes(pattern));
if (matches) {
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 any 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();
window.location.href = savedLink;
} catch (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();
try {
const userId = GM_getValue('userId', null);
if (userId) {
window.location.href = `https://www.myedio.com/directory/users/${userId}/`;
} else {
showErrorPopup(104, "User ID not found. Please complete the setup first.");
}
} catch (e) {
showErrorPopup(105, "Error fetching user ID.");
}
break;
case 'f':
event.preventDefault();
const videoElement = document.querySelector('video');
if (videoElement) {
if (!document.fullscreenElement) {
videoElement.requestFullscreen().catch(err => {
showErrorPopup(201, `Error enabling fullscreen on video: ${err.message}`);
});
} else {
document.exitFullscreen().catch(err => {
showErrorPopup(202, `Error exiting fullscreen mode: ${err.message}`);
});
}
} else {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => {
showErrorPopup(201, `Error enabling fullscreen mode: ${err.message}`);
});
} else {
document.exitFullscreen().catch(err => {
showErrorPopup(202, `Error exiting fullscreen mode: ${err.message}`);
});
}
}
break;
case 'm':
event.preventDefault();
const mediaElements = document.querySelectorAll('audio, video');
if (mediaElements.length > 0) {
const isMuted = Array.from(mediaElements).every(el => el.muted);
mediaElements.forEach(el => (el.muted = !isMuted));
} else {
showErrorPopup(302, "No audio or video elements found on the page to mute/unmute.");
}
break;
}
}
}
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="Edio_Hotkeys" target="" title="Hotkeys">
<span class="hotkeys-text">Hotkeys</span>
<img class="hotkeys-icon" src="https://raw.githubusercontent.com/MineverseTutorials/Userscripts/refs/heads/main/images/Hotkeys-Icon.png" alt="Hotkeys Icon" style="width: 20px; height: 20px; display: none;">
</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 textElement = hotkeysElement.querySelector('.hotkeys-text');
const iconElement = hotkeysElement.querySelector('.hotkeys-icon');
if (isExpanded) {
textElement.style.display = 'block';
iconElement.style.display = 'none';
} else {
textElement.style.display = 'none';
iconElement.style.display = 'block';
}
});
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 = `
<p style="color: gray; font-size: 12px;">Release-1</p>
<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>Profile:</strong> Alt + L</p>
<p><strong>Full Screen:</strong> Alt + F</p>
<p><strong>Mute/Unmute All Sound:</strong> Alt + M</p>
<p>Hello ${userName}! You are running a stable version of <strong>Edio Hotkeys</strong>.</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('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);
}
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: 12px;
width: 500px;
text-align: center;
animation: slideUp 0.3s ease-out;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
z-index: 1002;
}
.hotkeys-overlay h2 {
font-size: 24px;
margin-bottom: 15px;
font-weight: 600;
color: #333;
}
.hotkeys-overlay p {
font-size: 16px;
margin: 10px 0;
color: #666;
}
.hotkeys-overlay button {
margin-top: 25px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
transition: background-color 0.3s ease;
}
.hotkeys-overlay button:hover {
background-color: #0056b3;
cursor: pointer;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(30px); }
to { transform: translateY(0); }
}
.error-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;
}
.error-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;
}
.error-overlay-content {
background: #ffffff;
padding: 30px 25px;
border-radius: 12px;
width: 320px;
text-align: center;
animation: slideUp 0.3s ease-out;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
z-index: 1002;
}
.error-overlay h2 {
font-size: 24px;
margin-bottom: 15px;
font-weight: 600;
color: #333;
}
.error-overlay p {
font-size: 16px;
margin: 10px 0;
color: #666;
}
.error-overlay button {
margin-top: 25px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
transition: background-color 0.3s ease;
}
.error-overlay button:hover {
background-color: #0056b3;
cursor: pointer;
}
`;
document.head.appendChild(style);
saveLinkIfMatches();
window.addEventListener('keydown', handleHotkeys);
setInterval(() => {
addHotkeysElement();
}, 10);
})();