// ==UserScript==
// @name Fast Rise
// @namespace Violentmonkey Scripts
// @match https://rise.articulate.com/author/*
// @grant none
// @version 1.2.2
// @author AMC-Albert
// @description 2/14/2025, 4:22:13 PM
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const style = document.createElement('style');
style.textContent = `
/* Disable animations to make things snappy and instant, except on a few elements that break if their animations are removed */
*:not(.author-layout--process *, [data-rmiz-modal-content] *, .curtain *, .upload-progress *) {
animation: none !important;
transition: none !important;
}
/* Remove banner thumbnails from 'copy to another course' feature to make it load faster */
.copy-lesson-dialog__course-list__course-icon {
background-image: none !important;
}
/* Remove AI garbage */
button:has(.ai-gradient), .menu__item:has(.ai-gradient), .ai-tooltip-rich, .authoring-tooltip:has([src="https://cdn.articulate.com/assets/rise/assets/ai/block-wizard/generate-image-thumbnail.webp"]) {
display: none;
}
`;
document.head.appendChild(style);
const isCourseOutline = () => window.location.href.includes('/author/course');
const isSectionPage = () => window.location.href.includes('/author/details/');
const scrollKey = `scrollPos_${window.location.pathname}`;
const buttonKey = `lastClickedButton`;
const navKey = `sectionNav`;
const highlightColor = '#15095814';
console.log('Userscript loaded. Current URL:', window.location.href);
// Save scroll position every 500ms, only if on the course outline page
setInterval(() => {
if (isCourseOutline()) {
localStorage.setItem(scrollKey, window.scrollY);
console.log('Saved scroll position:', window.scrollY);
}
}, 500);
// Restore scroll position only if on the course outline page
window.addEventListener('load', () => {
if (isCourseOutline()) {
const savedScrollPos = localStorage.getItem(scrollKey);
console.log('Restoring scroll position:', savedScrollPos);
if (savedScrollPos !== null) {
setTimeout(() => {
window.scrollTo(0, parseInt(savedScrollPos, 10));
}, 100);
}
highlightLastClickedButton();
storeButtonHrefs();
} else if (isSectionPage()) {
updateLastClickedButton();
}
});
// Update last clicked button on popstate (browser back/forward)
window.addEventListener('popstate', () => {
if (isSectionPage()) {
updateLastClickedButton();
} else if (isCourseOutline()) {
highlightLastClickedButton();
}
});
// Override Articulate's scroll resets, but only on the course outline page
const originalScrollTo = window.scrollTo;
window.scrollTo = function(x, y) {
if (isCourseOutline() && y === 0) {
const savedScrollPos = localStorage.getItem(scrollKey);
if (savedScrollPos !== null) {
y = parseInt(savedScrollPos, 10);
console.log('Overriding scrollTo:', y);
}
}
originalScrollTo.call(this, x, y);
};
// Save the last clicked button's href to localStorage
function attachClickListeners() {
const buttons = document.querySelectorAll('a.course-outline-lesson__edit-content');
console.log('Found buttons:', buttons.length);
buttons.forEach(button => {
button.addEventListener('click', () => {
const href = button.getAttribute('href');
localStorage.setItem(buttonKey, href);
console.log('Button clicked, saving href:', href);
});
});
}
// Highlight the last clicked button
function highlightLastClickedButton() {
const lastClickedHref = localStorage.getItem(buttonKey);
console.log('Last clicked href:', lastClickedHref);
if (lastClickedHref) {
const button = document.querySelector(`a.course-outline-lesson__edit-content[href="${lastClickedHref}"]`);
if (button) {
console.log('Highlighting button:', button);
button.style.background = highlightColor;
} else {
console.log('Button not found for href:', lastClickedHref);
}
}
}
// Store all "Edit Content" button hrefs for navigation
function storeButtonHrefs() {
const buttons = document.querySelectorAll('a.course-outline-lesson__edit-content');
const hrefs = Array.from(buttons).map(button => button.getAttribute('href'));
localStorage.setItem(navKey, JSON.stringify(hrefs));
console.log('Stored button hrefs:', hrefs);
}
// Update the last clicked button when arriving at a section page
function updateLastClickedButton() {
const currentHref = window.location.hash;
localStorage.setItem(buttonKey, currentHref);
console.log('Updated last clicked button on section load:', currentHref);
}
// Keyboard shortcuts for section navigation
window.addEventListener('keydown', (e) => {
if (isSectionPage() && e.ctrlKey && e.altKey) {
const hrefs = JSON.parse(localStorage.getItem(navKey) || '[]');
const currentHref = window.location.hash;
const currentIndex = hrefs.indexOf(currentHref);
console.log('Current section index:', currentIndex);
let nextHref = null;
if (e.key === 'ArrowRight' && currentIndex < hrefs.length - 1) {
nextHref = hrefs[currentIndex + 1];
} else if (e.key === 'ArrowLeft' && currentIndex > 0) {
nextHref = hrefs[currentIndex - 1];
}
if (nextHref) {
localStorage.setItem(buttonKey, nextHref); // Update last clicked button when navigating
window.location.hash = nextHref;
console.log('Navigating to:', nextHref);
}
}
});
// Observe dynamic changes for React-rendered buttons
const observer = new MutationObserver(() => {
if (isCourseOutline()) {
attachClickListeners();
highlightLastClickedButton();
storeButtonHrefs();
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();