javascript:(function(){ /* Config variables. */ var heightTakeVideoNotes = /* set initial height for notes */ "50px"; /* Global DOM variables */ var noteAreaTakeNotesIDTakeNotes = /* parent for note elements */ document.getElementById("noteAreaTakeNotes"); var noteBoxTakNotes = /* textare element for taking notes */ document.getElementById("noteBoxTakNotes"); var closeNoteBoxTakeNotes = /* Close notebox element */ document.getElementById("closeNoteBoxTakeNotes"); var aboveTheFoldTakeNotes = /* html after vidoe box */ document.getElementById("above-the-fold"); var playerTakeNotes = /* video playerTakeNotes topmost parent */ document.getElementById("player"); var playButtonTakeNotes = /* play button - needed to update time mark */ document.getElementsByClassName("ytp-play-button"); var currentTimeClassNameTakeNotes = /* class element with current time */ "ytp-time-current"; /* Global configuration variables */ var ignoredKeysTakeNotes = /* keys pressed that do not active not box */ "Home End PageUp PageDown"; var ignoredDOMElementsTakeNotes = /* stop function if one of these is active */ ["comments", "search", "contenteditable-root", "player"]; /* CSS style sheet */ { var noteCSSTakeNotes = ` /* style close elements with constant styles */ button[id^="closeBMBtn"] { display: inline; position: relative; left: -5px; top: 5px; width: 25px; height: 25px; background: red; color: white; border: none; border-radius: 5px; z-index: 1; } button[id^="closeBMBtn"]:hover, button[id^="closeBMBtn"]:hover ~ input[id^="closeBMBtn"]:hover { cursor: pointer; border: 3px solid gray; } input[id^="closeBMBtn"] { box-sizing: content-box; display: inline-block; position: absolute; left: -5px; top: 5px; z-index: 2; opacity: 0; } input[id^="closeBMBtn"]:hover { cursor: pointer; width: 20px; height: 20px; border: 2px solid gray; opacity: .25; } input[data-close-bm-el^="hide"] + div { display: none !important; } input[data-close-bm-el^="show"] + div#noteArea { display: inline-flex; } /* style note area with constant styles */ div#noteAreaTakeNotes { display: inline-flex; flex-wrap: nowrap; position: relative; top: 0px; width: 100%; } div #noteAreaTakeNotes div { display: inline-block; position: relative; margin: 0px 10px; max-width: 450px; width: min-content; } div#noteAreaTakeNotes div+div { display: inline-block; position: relative; max-width: 800px; width: auto !important; left: 0px; } /* style note box with constant style */ textarea#noteBoxTakNotes { max-width: 450px; width: 450px; height: ${heightTakeVideoNotes}; padding: 10px; border-radius: 10px; } `; } /* Note box setup. */ var noteTextAreaTakeNotes, noteAreaTakeNotes, noteBoxTakNotesDiv; /************************************* SUPPORT FUNCTIONS *************************************/ /* Create show, hide buttons for elements. */ const insertShowHideBtnYouTubeTakeNotes = (el, curId, placeEl, placemnt) => { let closeNoteBoxBtn = document.createElement(el); closeNoteBoxBtn.id = "closeBMBtn" + curId; placeEl.insertAdjacentElement(placemnt, closeNoteBoxBtn); closeNoteBoxBtn.textContent = "X"; /* checkbox to show hide with css */ let closeCheckbox = document.createElement("input"); closeCheckbox.type = "checkbox"; closeCheckbox.id = "closeBMBtnCheckbox" + curId; closeNoteBoxBtn.insertAdjacentElement("afterend", closeCheckbox); closeCheckbox = document.getElementById("closeBMBtnCheckbox" + curId); closeCheckbox.setAttribute("checked", true); /* HOT-GLUE */ if (placeEl.hasAttribute("id") && placeEl.getAttribute("id") == "player") { closeCheckbox.style.position = "relative"; closeCheckbox.style.left = "-30px"; } /* alternate close button status */ closeCheckbox.addEventListener("mousedown", function() { if (this.previousElementSibling.textContent == "X") { this.previousElementSibling.textContent = "O"; /* HOT-GLUE */ if (placeEl.hasAttribute("id") && placeEl.getAttribute("id") == "player") { player.style.display = "none"; } else { this.setAttribute("data-close-bm-el", "hide"); } } else { this.previousElementSibling.textContent = "X"; /* HOT-GLUE */ if (placeEl.hasAttribute("id") && placeEl.getAttribute("id") == "player") { player.style.display = ""; } else { this.setAttribute("data-close-bm-el", "show"); } } }); }; /* Create note box, ensuring not to duplicate. */ const addNoteBoxYouTubeTakeNotes = () => { if (!noteAreaTakeNotesIDTakeNotes) { /* are for notes */ noteAreaTakeNotes = document.createElement("div"); noteAreaTakeNotes.id = "noteAreaTakeNotes"; aboveTheFoldTakeNotes.insertAdjacentElement("beforebegin", noteAreaTakeNotes); let noteStyle = /* using noteCSSTakeNotes from above */ document.createElement("style"); noteStyle.textContent = /* add css properties */ noteCSSTakeNotes; noteAreaTakeNotes.insertAdjacentElement("beforebegin", noteStyle); noteAreaTakeNotesIDTakeNotes = document.getElementById("noteAreaTakeNotes"); /* note box */ noteTextAreaTakeNotes = document.createElement("textarea"); noteTextAreaTakeNotes.id = "noteBoxTakNotes"; noteBoxTakNotesDiv = document.createElement("div"); /* insert div to hold textarea */ noteAreaTakeNotesIDTakeNotes.insertAdjacentElement("afterbegin", noteBoxTakNotesDiv); /* insert textare html elements to take notes */ noteBoxTakNotesDiv.insertAdjacentElement("afterbegin", noteTextAreaTakeNotes); /* close buttnon */ insertShowHideBtnYouTubeTakeNotes( "button", /* insert button */ "NoteBoxBtn", /* set closeBMBtn += NoteBoxBtn */ noteStyle, /* placement based on element */ "afterend" /* placement after element */ ); /* close button for video */ insertShowHideBtnYouTubeTakeNotes( "button", /* insert button */ "VideoPlayer", /* set closeBMBtn += NoteBoxBtn */ playerTakeNotes, /* placement based on element */ "beforebegin" /* placement before element */ ); /* Redefine noteBoxTakNotes */ noteBoxTakNotes = document.getElementById("noteBoxTakNotes"); } }; /* Copy notes in textare to clipboard. */ const copyNotesTakeNotes = () => { noteBoxTakNotes.select(); navigator.clipboard.writeText(noteBoxTakNotes.value); }; /* Check that another key is not pressed down before moving cursor outside of notebox. */ const checkKeydownYouTubeTakeVideoNotes = () => { if (event.key == "Shift" || event.key == "Control") { return true; } else { return false; } }; /* Check if Control and Shift are sequentially keyup and keydown under 1 second. */ const checkCtrlShiftYouTubeTakeVideoNotes = (ms = 750) => { return new Promise(resolve => { let ctrlPressed = false; let ctrlReleased = false; let shiftPressed = false; let shiftReleased = false; let deadline = 0; let timeoutId = null; function cleanup(result) { document.removeEventListener('keydown', onKeyDown, true); document.removeEventListener('keyup', onKeyUp, true); if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } resolve(!!result); } function startTimeout() { /* start or restart the timeout for the shift window */ if (timeoutId) clearTimeout(timeoutId); deadline = Date.now() + ms; timeoutId = setTimeout(() => { /* timed out waiting for shift sequence */ cleanup(false); }, ms); } function onKeyDown(e) { const k = e.key; if (k === 'Control') { /* ctrl pressed */ ctrlPressed = true; } else if (k === 'Shift') { /* only pay attention to shift if ctrl has already been released */ if (ctrlReleased && !shiftPressed) { shiftPressed = true; } } } function onKeyUp(e) { const k = e.key; if (k === 'Control') { /* only consider a ctrl "release" if it was pressed first */ if (ctrlPressed) { ctrlPressed = false; ctrlReleased = true; startTimeout(); } } else if (k === 'Shift') { /* only consider shift release if we saw a shift press after ctrl release */ if (shiftPressed) { shiftPressed = false; shiftReleased = true; /* check timing and that neither key is currently held down */ const withinTime = Date.now() <= deadline; const ctrlStillHeld = ctrlPressed; /* should be false */ const shiftStillHeld = shiftPressed; /* should be false */ if (withinTime && !ctrlStillHeld && !shiftStillHeld) { cleanup(true); } else { cleanup(false); } } } } /* attach capturing listeners so they catch keys even if elements stop propagation */ document.addEventListener('keydown', onKeyDown, true); document.addEventListener('keyup', onKeyUp, true); /* safety: if nothing happens for (ms + 1000) ms, give up */ const globalTimeout = setTimeout(() => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } cleanup(false); }, ms + 1000); /* ensure the safety timeout cleared on final cleanup */ const origCleanup = cleanup; cleanup = function(result) { clearTimeout(globalTimeout); origCleanup(result); }; }); }; /* Focus on textarea whenever keydown occurs. */ const keypressToNoteTakeNotes = () => { let activeID = document.activeElement.id; let lastKeyPressTakeNotes; /* used to check for key combos */ lastKeyPressTakeNotes = sessionStorage.getItem("lastKeyPressTakeNotes"); /* for first key press */ if (lastKeyPressTakeNotes == null) { sessionStorage.setItem("lastKeyPressTakeNotes", event.key); lastKeyPressTakeNotes = sessionStorage.getItem("lastKeyPressTakeNotes"); } /* store key press and check active element */ let currentKeyPress = event.key; /* start conditions to activate note box or run function accordingly */ /* if any of these elements have focus */ /** ignoredDOMElementsTakeNotes = "comments search contenteditable-root playerTakeNotes"; **/ /* then don't take notes - note - variable defined at start */ for (i in ignoredDOMElementsTakeNotes) { if (activeID == ignoredDOMElementsTakeNotes[i]) { /* quit function */ return; } } /* else */ { /* the active element is not in ignored list, run function */ let checkKeyCombo = /* check for combos */ lastKeyPressTakeNotes + "+" + currentKeyPress; /* check key combos and run function accordingly */ checkCtrlShiftYouTubeTakeVideoNotes(750).then((ok) => { /* checkKeyCombo = "Control Shift" */ if (ok) { noteBoxTakNotes.blur(); } else { let skip; /* do nothing */ } }); /* select and copy notes to clipboard */ if (checkKeyCombo == "Alt+a") { copyNotesTakeNotes(); } else { /* only if note box is not active element */ if (activeID != "noteBoxTakNotes") { /* prevent fullscreen from occuring *//* ************************************** NOTE - THIS DOES NOT WORK ************************* if (currentKeyPress == "f") { document.activeElement.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Esc'})); } ************************************** ***************************************/ /* USE VARIABLE FOR CONDITIONS */ let noIgnoredKeys = ignoredKeysTakeNotes.indexOf(event.key) == -1; let notPauseShortcut = checkKeyCombo != "Shift+ "; if (noIgnoredKeys) { if (notPauseShortcut) { noteBoxTakNotes.focus(); } } } } } /* store key press for next key combo check */ sessionStorage.setItem("lastKeyPressTakeNotes", event.key); }; /* Quickly get back to notes */ const addKeyDownTakeNotes = () => { document.body.addEventListener("keydown", function() { keypressToNoteTakeNotes(); }); }; /********************************************************************************************* MAIN FUNCTION *********************************************************************************************/ function takeVideoNotes() { /* add note box */ addNoteBoxYouTubeTakeNotes(); /* begin taking notes */ noteBoxTakNotes.focus(); /* listen for keydown event */ addKeyDownTakeNotes(); } /* Call main function. */ takeVideoNotes(); })();