Streamlined login experience and localStorage-based preservation of comments.
当前为
// ==UserScript==
// @name BootCampSpot v2 Improvements
// @namespace https://jonas.ninja
// @version 1.1.1
// @description Streamlined login experience and localStorage-based preservation of comments.
// @author @iv_njonas
// @match https://bootcampspot-v2.com/*
// @grant none
// ==/UserScript==
(function preserveCommentsInLocalStorage() {
/// BCS has a nasty habit of kicking out users after a certain time, usually a few moments
/// before they submit a long and thoughtful comment on a student's homework. This has been
/// a great source of workplace stress and psychological harm. This code prevents such
/// heartache by auto-saving your comments at every keystroke, and presents a button to
/// easily load a saved comment after BCS kicks you out.
var localStorageKey = 'bootcampspotv2-comment';
var commentBoxSelector = 'textarea#commentEntry';
var loadButton = createLoadButton();
var infoAlert = createInfoAlert();
var noSavedComment = createNoSavedCommentAlert();
var oldText;
$('body').on('keyup', commentBoxSelector, function(e) {
// save the entered text if it has changed significantly. If the button happens to be visible, hide it.
// give the user some feedback that their comments are saved.
alert('keyup handler');
var text = e.target.value;
if (text.length < 10) {
return;
} else {
// read oldText from cache or from localStorage, and if it has not changed, don't do anything.
oldText = oldText || window.localStorage.getItem(localStorageKey);
if (oldText === text) {
return;
}
}
window.localStorage.setItem(localStorageKey, e.target.value);
loadButton.remove();
if (e.target.value === '') {
infoAlert.remove();
e.target.insertAdjacentElement('afterend', noSavedComment);
} else {
noSavedComment.remove();
e.target.insertAdjacentElement('afterend', infoAlert);
}
});
// inject a button that will load the comment
window.setInterval(function() {
var commentBox = document.querySelector(commentBoxSelector);
if (!commentBox || commentBox.dataset.ijgDecorated) {
return;
} else {
commentBox.dataset.ijgDecorated = true;
// insert the load button if the comment box is empty
if (commentBox.value === '' && window.localStorage.getItem(localStorageKey)) {
commentBox.insertAdjacentElement('afterend', loadButton);
}
}
}, 1000);
function createLoadButton() {
var loadButton = document.createElement('div');
loadButton.classList = 'btn btn-lg bcs-button ijg-bcs-loadButton';
loadButton.style.position = 'absolute';
loadButton.style.top = '20px';
loadButton.style.left = '50%';
loadButton.style.transform = 'translateX(-50%)';
loadButton.onclick = loadButtonClickHandler;
loadButton.textContent = "Load comment from localStorage";
return loadButton;
}
function createInfoAlert() {
var alertDiv = document.createElement('div');
alertDiv.classList = 'alert alert-info pull-left';
alertDiv.style.marginTop = '10px';
alertDiv.style.marginBottom = '-7px';
alertDiv.textContent = 'Your comment is saved in localStorage!';
return alertDiv;
}
function createNoSavedCommentAlert() {
var infoDiv = createInfoAlert();
infoDiv.classList = 'alert alert-warning pull-left';
infoDiv.textContent = 'No comments in localStorage...';
return infoDiv;
}
function loadButtonClickHandler() {
// when the load button is clicked, the contents of the comment box are replaced
// and the button disappears permanently
document.querySelector(commentBoxSelector).value = window.localStorage.getItem(localStorageKey);
loadButton.remove();
}
})();
(function streamlineLogin() {
/// There is a pointless mandatory "click to login" button. Click that right away.
/// Then wait for the browser's autofill and
var loginWithUsernameIntervalId;
var loginFormIntervalId;
var autofillIntervalId;
var autofillWaitingPeriodInMS = 1000;
var autofillWaitingPeriodStartTime;
loginWithUsernameIntervalId = window.setInterval(attemptClickLoginWithUsername, 100);
/// Phase 1: wait for the first form to appear, or confirm that it will never appear.
function attemptClickLoginWithUsername() {
var loginWithUsernameButton = document.querySelector('.landing .btn-login');
if (loginWithUsernameButton === null) {
// Either the page isn't loaded yet, or we're already logged in.
// If already logged in, clear all intervals and exit.
if ($('.header-menu .logout').length) {
window.clearInterval(loginWithUsernameIntervalId);
}
} else {
// The button is there. This function is done. Onward to the next phase.
window.clearInterval(loginWithUsernameIntervalId);
loginWithUsernameButton.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true}));
loginFormIntervalId = window.setInterval(waitForLoginForm, 100);
}
}
/// Phase 2: wait for the second form to appear.
function waitForLoginForm() {
var loginButton = document.querySelector('.landing button[type="submit"]');
var usernameField = document.getElementById('email');
var passwordField = document.getElementById('password');
if (loginButton !== null && usernameField !== null && passwordField !== null) {
// Onward to next phase.
window.clearInterval(loginFormIntervalId);
autofillIntervalId = window.setInterval(waitForAutofill, 100);
}
if ($('.header-menu .logout').length) {
window.clearInterval(loginFormIntervalId);
}
// if this function is fired once, it is guaranteed that it will hit its success
// condition and clear its own interval.
}
/// Phase 3: wait for autofill to kick in, or timeout and exit.
function waitForAutofill() {
var loginButton = document.querySelector('.landing button[type="submit"]');
var usernameField = document.querySelector('#email');
var passwordField = document.querySelector('#password');
autofillWaitingPeriodStartTime = autofillWaitingPeriodStartTime || new Date();
var now = new Date();
if (now.getTime() > autofillWaitingPeriodStartTime.getTime() + autofillWaitingPeriodInMS) {
// timeout triggered. Exit program.
window.clearInterval(autofillIntervalId);
} else if (usernameField.value.length > 0) {
// autofill worked. Program completed successfully.
window.clearInterval(autofillIntervalId);
var style = loginButton.style;
style.position = 'fixed';
style.width = style.height = '100%';
style.top = style.left = 0;
style.fontSize = '30vmin';
loginButton.onmousemove = hoverHandler;
}
}
function hoverHandler() {
// this only works on Firefox because of a Chrome security "feature"
var loginButton = document.querySelector('.landing button[type="submit"]');
loginButton.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true}));
}
})();