Skip to content

Instantly share code, notes, and snippets.

@BlueFalconHD
Created February 18, 2025 20:45
Show Gist options
  • Save BlueFalconHD/0d146affd67f7f1d3aa79a5899016995 to your computer and use it in GitHub Desktop.
Save BlueFalconHD/0d146affd67f7f1d3aa79a5899016995 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Quill.org Enhanced Interaction Script
// @namespace https://www.quill.org/
// @version 1.5
// @description Pressing Enter clicks "Next Question", "Submit", or "Recheck Work" buttons on quill.org. Adds a progress bar on completion and auto-focuses on input fields.
// @author
// @match *://*.quill.org/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
// Function to handle keydown events
function handleKeyDown(event) {
// Check if the Enter key is pressed without modifier keys
if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
// Prevent default action
event.preventDefault();
// Find all button elements
const buttons = document.getElementsByTagName('button');
// Function to find a button by text (case-insensitive)
function findButtonByText(text) {
text = text.toLowerCase();
for (let button of buttons) {
if (button.innerText.trim().toLowerCase() === text) {
return button;
}
}
return null;
}
// Try to find the "Next Question" button first
let targetButton = findButtonByText('next question');
// If "Next Question" button is not found, try to find the "Submit" button
if (!targetButton) {
targetButton = findButtonByText('submit');
}
// If neither is found, try to find the "Recheck Work" button
if (!targetButton) {
targetButton = findButtonByText('recheck work');
}
// If none are found, try to find the "Begin" button
if (!targetButton) {
targetButton = findButtonByText('begin');
}
// Click the target button if found
if (targetButton) {
targetButton.click();
focusFirstInputDelayed();
}
}
}
// Function to auto-focus on the necessary input field after a delay
function focusFirstInputDelayed() {
setTimeout(function () {
// Try to focus on input elements with class 'fill-in-blank-input' or visible <input type="text">
const inputElements = document.querySelectorAll('input.fill-in-blank-input, input[type="text"]:not([type="hidden"])');
for (let input of inputElements) {
if (input.offsetParent !== null) { // Check if the input is visible
input.focus();
return; // Stop after focusing the first visible input
}
}
// If no input found, fall back to 'div.input-field'
const firstInputField = document.querySelector('div.input-field');
if (firstInputField) {
firstInputField.focus();
}
}, 25); // Delay between 20-30ms
}
// Function to initialize the progress bar on activity completion
function initializeCompletionProgressBar() {
// Check for an <h1> element with the text "Activity Complete!" (case-insensitive)
const h1Elements = document.getElementsByTagName('h1');
let activityCompleteHeader = null;
for (let h1 of h1Elements) {
if (h1.innerText.trim().toLowerCase() === 'activity complete!') {
activityCompleteHeader = h1;
break;
}
}
// If the "Activity Complete!" header exists
if (activityCompleteHeader) {
// Find the <a> element with the text "Return to Dashboard" (case-insensitive)
const aElements = document.getElementsByTagName('a');
let returnToDashboardLink = null;
for (let a of aElements) {
if (a.innerText.trim().toLowerCase() === 'return to dashboard') {
returnToDashboardLink = a;
break;
}
}
// If the "Return to Dashboard" link is found
if (returnToDashboardLink) {
// Apply initial styling to the link
returnToDashboardLink.style.position = 'relative';
returnToDashboardLink.style.display = 'inline-block';
returnToDashboardLink.style.overflow = 'hidden';
returnToDashboardLink.style.backgroundColor = '#06806b';
returnToDashboardLink.style.color = 'white';
// Create a progress bar overlay
const progressBar = document.createElement('div');
progressBar.style.position = 'absolute';
progressBar.style.top = '0';
progressBar.style.left = '0';
progressBar.style.height = '100%';
progressBar.style.width = '0%';
progressBar.style.backgroundColor = 'rgb(78, 165, 0)';
progressBar.style.zIndex = '-1';
progressBar.style.transition = 'width 1s linear';
// Append the progress bar to the link
returnToDashboardLink.appendChild(progressBar);
// Force a reflow to ensure the progress bar is rendered before starting the animation
progressBar.offsetWidth; // eslint-disable-line no-unused-expressions
// Start the progress bar animation
requestAnimationFrame(function () {
progressBar.style.width = '100%';
});
// Redirect to the link's URL after 1 second
setTimeout(function () {
window.location.href = returnToDashboardLink.href;
}, 1000);
}
}
}
// Function to add click event listeners to buttons
function addButtonClickListeners() {
// Function to add the listener to a button if it exists
function addListenerToButton(text) {
const buttons = document.getElementsByTagName('button');
for (let button of buttons) {
if (button.innerText.trim().toLowerCase() === text) {
button.addEventListener('click', function () {
focusFirstInputDelayed();
});
break; // Exit after adding listener to the first matched button
}
}
}
// Add listeners to the buttons
addListenerToButton('next question');
addListenerToButton('submit');
addListenerToButton('recheck work');
addListenerToButton('begin');
}
// Add event listeners
document.addEventListener('keydown', handleKeyDown);
// Run the completion progress bar initialization on page load
window.addEventListener('load', function () {
initializeCompletionProgressBar();
addButtonClickListeners();
});
// Observe DOM mutations to handle dynamic content changes
const observer = new MutationObserver(function () {
addButtonClickListeners();
});
observer.observe(document.body, { childList: true, subtree: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment