Last active
August 7, 2019 20:37
-
-
Save cuylerstuwe/d9cc4a9140ed0e6461d8c86c7517186a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name mTurk Multibind (Key, MIDI CC 50-57, Gamepad) (+Voice coming soon) => Radio | |
| // @namespace salembeats | |
| // @version 7.5 | |
| // @description Works with groups and checkboxes now too. Latest update: Ability to permanently disable or permanently enable on a GID-by-GID basis. | |
| // @author Cuyler Stuwe (salembeats) | |
| // @icon data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIANgA2AAD/2wBDABoSExcTEBoXFRcdGxofJ0AqJyMjJ084PC9AXVJiYVxSWllndJR+Z22Mb1laga+CjJmepqemZHy2w7ShwZSjpp//2wBDARsdHSciJ0wqKkyfalpqn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5//wgARCAAgACADAREAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAwQAAQIF/8QAFwEAAwEAAAAAAAAAAAAAAAAAAQIDAP/aAAwDAQACEAMQAAABNeVjDYVsQGbLggUtVQKMhGnQvLZwUOQWqJ//xAAbEAACAwEBAQAAAAAAAAAAAAABEQACAxIxEP/aAAgBAQABBQLljiefGExL6ADD1zUqszrzUt3FrwZQef/EABsRAAIDAAMAAAAAAAAAAAAAAAABAhAREiAh/9oACAEDAQE/AcM6eCTJVGm6WI5V/8QAGhEAAgMBAQAAAAAAAAAAAAAAAAECEBESIf/aAAgBAgEBPwHo6NrGejawgqm/KisQxpsUBH//xAAZEAACAwEAAAAAAAAAAAAAAAAAAREgMSH/2gAIAQEABj8CphgxusoyBydP/8QAHhABAQEAAQQDAAAAAAAAAAAAAREAIRBBUWExcZH/2gAIAQEAAT8hJEdXkyU34NSzviYehBFqkhvwpkE955nfp9w84Ca8SaMTHlznIcnE1g4X1v/aAAwDAQACAAMAAAAQD/iBGlDS/8QAGhEAAwADAQAAAAAAAAAAAAAAAAERECFBMf/aAAgBAwEBPxBWsZuEqKpCiioeKEoleLMTUjG6DTg/dH//xAAZEQEBAQEBAQAAAAAAAAAAAAABEQAhURD/2gAIAQIBAT8QYY6fNEusZc0UdPWJDiWuUMhx9wNpuXJl7dU7v//EACIQAQEAAgIBAwUAAAAAAAAAAAERACExQWFRcZEQscHh8f/aAAgBAQABPxBthIrSf3G2pWF95hUCFXxi41GUDs7zY4nLPfCIVwV77/WHg0fdqN9MKAZ9l3+DGQ28ALghYuXx9I99d48YYEyiZ31gMyrRb8YcOKUdPxgK0CPTP//Z | |
| // @include * | |
| // @require https://greasyfork.org/scripts/33041-mturk-frame-parent-interface-library/code/mTurk%20Frame-%3EParent%20Interface%20Library.js?version=239183 | |
| // @grant GM_getValue | |
| // @grant GM_setValue | |
| // @grant unsafeWindow | |
| // ==/UserScript== | |
| // TODO: Rename radio-button-specific variables and methods to reflect that this script also works with checkboxes now. | |
| // TODO: Fix zooming to always try to keep centered content in the center at all scroll levels. | |
| function isInMturkIframe() { | |
| return ( window !== window.top && (document.referrer.includes("mturk.com/mturk/preview") || document.referrer.includes("mturk.com/mturk/accept") || document.referrer.includes("mturk.com/mturk/continue") || document.referrer.includes("mturk.com/mturk/return") || document.referrer.includes("mturk.com/mturk/previewandaccept") || document.referrer.includes("worker.mturk.com/projects/"))); | |
| } | |
| if (!isInMturkIframe()) {return;} | |
| const gid = document.referrer.match(/projects\/(3[A-Za-z0-9]{29})/)[1]; | |
| const gidStatuses = JSON.parse(GM_getValue('gidStatuses') || "{}"); | |
| function restoreKeybindsToGid() { | |
| gidStatuses[gid] = undefined; | |
| GM_setValue('gidStatuses', JSON.stringify(gidStatuses)); | |
| } | |
| if(gidStatuses[gid] === 'disabled') { | |
| unsafeWindow.restoreKeybindsToGid = restoreKeybindsToGid; | |
| return; | |
| } | |
| // This method of inserting styles has demonstrated 1 rare instance of crashing the latest version of Chrome (HIT 3EM4DVSA9VCUF872SQS7MYM8S5830K). | |
| // Commenting out. | |
| // document.styleSheets[0].insertRule('@keyframes breathingRadios { 0%: {transform: scale(1);} 50% {transform: scale(1.3);} 100% {transform: scale(1);} }'); | |
| // document.styleSheets[0].insertRule('input[type="radio"].salemFocusedRadio { animation: breathingRadios 0.33s linear infinite; }'); | |
| // document.styleSheets[0].insertRule('input[type="checkbox"].salemFocusedRadio { animation: breathingRadios 0.33s linear infinite; }'); | |
| // New style insertion method to work around crashing bug. | |
| document.head.insertAdjacentHTML("beforeend", ` | |
| <style> | |
| @keyframes breathingRadios { | |
| 0%: {transform: scale(1);} | |
| 50% {transform: scale(1.3);} | |
| 100% {transform: scale(1);} | |
| } | |
| input[type="radio"].salemFocusedRadio { | |
| animation: breathingRadios 0.33s linear infinite !important; | |
| } | |
| input[type="checkbox"].salemFocusedRadio { | |
| animation: breathingRadios 0.33s linear infinite !important; | |
| } | |
| </style> | |
| `); | |
| var allRadios = document.querySelectorAll("input[type='radio'],input[type='checkbox']"); | |
| var radioGroups = [{groupName: "ungroupedRadios", radios: []}]; | |
| for(let radio of allRadios) { | |
| if(radio.getAttribute("name") === undefined || radio.getAttribute("name") === "") { // If radio isn't grouped. | |
| radioGroups[0].radios.push(radio); | |
| } | |
| else if(radioGroups[radioGroups.length-1].groupName !== radio.getAttribute("name")) { // If radio's group is different than group at the end of the array. | |
| radioGroups.push({groupName: radio.getAttribute("name"), radios: [radio]}); | |
| radioGroups[radioGroups.length-1].radios[0].insertAdjacentHTML("beforebegin",`<span style='font-weight: bold'>1: </span>`); | |
| } | |
| else { // If radio belongs in the group at the end of the array. | |
| radioGroups[radioGroups.length-1].radios.push(radio); | |
| radioGroups[radioGroups.length-1].radios[radioGroups[radioGroups.length-1].radios.length-1].insertAdjacentHTML("beforebegin",`<span style='font-weight: bold'>${radioGroups[radioGroups.length-1].radios.length}: </span>`); | |
| } | |
| } | |
| // Remove the initial ungrouped section if all radios have groups. | |
| if(radioGroups[0].radios.length === 0) {radioGroups.shift();} | |
| function hasRadios() { | |
| return radioGroups.length > 0; | |
| } | |
| var selectedRadioGroupIndex; | |
| var selectedRadioGroup; | |
| if(hasRadios()) { | |
| selectedRadioGroupIndex = 0; | |
| selectedRadioGroup = radioGroups[selectedRadioGroupIndex]; | |
| } | |
| else { return; } | |
| function highlightSelectedRadioGroup() { | |
| for(let radio of selectedRadioGroup.radios) { | |
| radio.classList.add("salemFocusedRadio"); | |
| } | |
| } | |
| function unhighlightSelectedRadioGroup() { | |
| for(let radio of selectedRadioGroup.radios) { | |
| radio.classList.remove("salemFocusedRadio"); | |
| } | |
| } | |
| const SB_LOGO = "/9j/4AAQSkZJRgABAQIANgA2AAD/2wBDABoSExcTEBoXFRcdGxofJ0AqJyMjJ084PC9AXVJiYVxSWllndJR+Z22Mb1laga+CjJmepqemZHy2w7ShwZSjpp//2wBDARsdHSciJ0wqKkyfalpqn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5//wgARCAAgACADAREAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAwQAAQIF/8QAFwEAAwEAAAAAAAAAAAAAAAAAAQIDAP/aAAwDAQACEAMQAAABNeVjDYVsQGbLggUtVQKMhGnQvLZwUOQWqJ//xAAbEAACAwEBAQAAAAAAAAAAAAABEQACAxIxEP/aAAgBAQABBQLljiefGExL6ADD1zUqszrzUt3FrwZQef/EABsRAAIDAAMAAAAAAAAAAAAAAAABAhAREiAh/9oACAEDAQE/AcM6eCTJVGm6WI5V/8QAGhEAAgMBAQAAAAAAAAAAAAAAAAECEBESIf/aAAgBAgEBPwHo6NrGejawgqm/KisQxpsUBH//xAAZEAACAwEAAAAAAAAAAAAAAAAAAREgMSH/2gAIAQEABj8CphgxusoyBydP/8QAHhABAQEAAQQDAAAAAAAAAAAAAREAIRBBUWExcZH/2gAIAQEAAT8hJEdXkyU34NSzviYehBFqkhvwpkE955nfp9w84Ca8SaMTHlznIcnE1g4X1v/aAAwDAQACAAMAAAAQD/iBGlDS/8QAGhEAAwADAQAAAAAAAAAAAAAAAAERECFBMf/aAAgBAwEBPxBWsZuEqKpCiioeKEoleLMTUjG6DTg/dH//xAAZEQEBAQEBAQAAAAAAAAAAAAABEQAhURD/2gAIAQIBAT8QYY6fNEusZc0UdPWJDiWuUMhx9wNpuXJl7dU7v//EACIQAQEAAgIBAwUAAAAAAAAAAAERACExQWFRcZEQscHh8f/aAAgBAQABPxBthIrSf3G2pWF95hUCFXxi41GUDs7zY4nLPfCIVwV77/WHg0fdqN9MKAZ9l3+DGQ28ALghYuXx9I99d48YYEyiZ31gMyrRb8YcOKUdPxgK0CPTP//Z"; | |
| const MIDI_TYPE_CC = 176; | |
| const MIDI_BUTTON_PRESS_VALUE = 127; | |
| var midi, midiData; | |
| var midiCCState = {}; | |
| if (navigator.requestMIDIAccess) { | |
| navigator.requestMIDIAccess({ | |
| sysex: false | |
| }).then(onMIDISuccess, onMIDIFailure); | |
| } else { | |
| console.log("No MIDI support exists in this browser."); | |
| } | |
| function onMIDISuccess(midiAccess) { | |
| midi = midiAccess; | |
| var inputs = midi.inputs.values(); | |
| for (var input = inputs.next(); input && !input.done; input = inputs.next()) { | |
| input.value.onmidimessage = onMIDIMessage; | |
| } | |
| } | |
| function onMIDIFailure(error) { | |
| console.log("MIDI support exists in this browser, but the permission request failed."); | |
| } | |
| function onMIDIMessage(message) { | |
| midiData = message.data; | |
| let cmd = midiData[0] >> 4, | |
| type = midiData[0] & 0xf0, | |
| channel = midiData[0] & 0xF, | |
| noteOrCC = midiData[1], | |
| value = midiData[2]; | |
| if(type === MIDI_TYPE_CC) { | |
| updateMIDICCState(channel, noteOrCC, value); | |
| } | |
| } | |
| function updateMIDICCState(channel,cc,value) { | |
| let isNewState = false; | |
| if(midiCCState[channel] === undefined) { | |
| midiCCState[channel] = {}; | |
| isNewState = true; | |
| } | |
| if(midiCCState[channel][cc] === undefined) { | |
| midiCCState[channel][cc] = value; | |
| isNewState = true; | |
| } | |
| if(isNewState) { | |
| onNewMIDICCState(channel,cc,value); | |
| return; | |
| } | |
| if(midiCCState[channel][cc] !== value) { | |
| midiCCState[channel][cc] = value; | |
| onNewMIDICCState(channel,cc,value); | |
| } | |
| } | |
| const CC_STARTING_POINT = 50; | |
| function onNewMIDICCState(channel,cc,value) { | |
| if(value === MIDI_BUTTON_PRESS_VALUE) { | |
| switch(cc) { | |
| case 50: | |
| case 51: | |
| case 52: | |
| case 53: | |
| case 54: | |
| case 55: | |
| case 56: | |
| case 57: | |
| clickCurrentRadioGroupButtonByIndex(cc - CC_STARTING_POINT); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| window.addEventListener("gamepadconnected", function(e) { | |
| var gp = navigator.getGamepads()[e.gamepad.index]; | |
| console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", | |
| gp.index, gp.id, | |
| gp.buttons.length, gp.axes.length); | |
| }); | |
| var gamepadState = { | |
| XBOX_MAPPING: { | |
| A: 0, | |
| B: 1, | |
| X: 2, | |
| Y: 3, | |
| LB: 4, | |
| RB: 5, | |
| LT: 6, | |
| RT: 7, | |
| BACK: 8, | |
| START: 9, | |
| DPAD: { | |
| UP: 12, | |
| DOWN: 13, | |
| LEFT: 14, | |
| RIGHT: 15 | |
| }, | |
| JOYSTICKS: { | |
| DEADZONE: 0.30, | |
| LEFT: { | |
| BUTTON: 10, | |
| AXIS: { | |
| X: 0, | |
| Y: 1 | |
| } | |
| }, | |
| RIGHT: { | |
| BUTTON: 11, | |
| AXIS: { | |
| X: 2, | |
| Y: 3 | |
| } | |
| } | |
| } | |
| }, | |
| buttons: { | |
| "0": 0, | |
| "1": 0, | |
| "2": 0, | |
| "3": 0, | |
| "4": 0, | |
| "5": 0, | |
| "6": 0, | |
| "7": 0, | |
| "8": 0, | |
| "9": 0, | |
| "10": 0, | |
| "11": 0, | |
| "12": 0, | |
| "13": 0, | |
| "14": 0, | |
| "15": 0 | |
| }, | |
| axes: { | |
| "0": 0, | |
| "1": 0, | |
| "2": 0, | |
| "3": 0 | |
| } | |
| }; | |
| function pollGamepad(gamepad) { | |
| for(let [index, button] of gamepad.buttons.entries()) { | |
| if(button.value !== gamepadState.buttons[index]) { | |
| if(button.value === 0) { | |
| onGamepadButtonReleased(index); | |
| } | |
| else { | |
| onGamepadButtonPressed(index); | |
| } | |
| } | |
| gamepadState.buttons[index] = button.value; | |
| } | |
| let deadzone = gamepadState.XBOX_MAPPING.JOYSTICKS.DEADZONE; | |
| for(let [index, axisValue] of gamepad.axes.entries()) { | |
| let newAxisDistance = Math.abs(axisValue); | |
| let oldAxisDistance = Math.abs(gamepadState.axes[index]); | |
| let isAxisLive = (newAxisDistance > deadzone); | |
| if(isAxisLive) {repeatOnActiveAxisState(index, axisValue);} | |
| if(axisValue !== gamepadState.axes[index]) { | |
| if( isAxisLive ) { | |
| onLiveJoystickPositionChange(index, axisValue); | |
| } | |
| else if( oldAxisDistance >= deadzone ) { | |
| onDeadJoystickPosition(index, axisValue); | |
| } | |
| } | |
| gamepadState.axes[index] = axisValue; | |
| } | |
| } | |
| function isPressedButtonRadioSelector(buttonIndex) { | |
| return ( | |
| buttonIndex !== gamepadState.XBOX_MAPPING.START && | |
| buttonIndex !== gamepadState.XBOX_MAPPING.BACK | |
| ); | |
| } | |
| function onGamepadButtonPressed(buttonIndex) { | |
| if(buttonIndex === gamepadState.XBOX_MAPPING.START) { | |
| useController.click(); | |
| } | |
| if(useController.checked && isPressedButtonRadioSelector(buttonIndex)) { | |
| clickCurrentRadioGroupButtonByIndex(buttonIndex); | |
| } | |
| } | |
| function onGamepadButtonReleased(buttonIndex) { | |
| } | |
| function onLiveJoystickPositionChange(axisIndex, value) { | |
| } | |
| function onDeadJoystickPosition(axisIndex, value) { | |
| } | |
| const CONTROLLER_MAX_SCROLL_RATE = 20; | |
| const CONTROLLER_MAX_ZOOM_RATE = 3.5; | |
| var customZoomPercent = 100; | |
| function repeatOnActiveAxisState(axisIndex, value) { | |
| if(useController.checked) { | |
| if( axisIndex === gamepadState.XBOX_MAPPING.JOYSTICKS.LEFT.AXIS.Y) { | |
| window.scrollBy(0, CONTROLLER_MAX_SCROLL_RATE * value); | |
| } | |
| if( axisIndex === gamepadState.XBOX_MAPPING.JOYSTICKS.RIGHT.AXIS.Y) { | |
| customZoomPercent += (CONTROLLER_MAX_ZOOM_RATE * value); | |
| if(customZoomPercent < 1) {customZoomPercent = 1;} | |
| document.body.style.zoom = `${customZoomPercent}%`; | |
| } | |
| } | |
| } | |
| function onTick() { | |
| let firstGamepad = navigator.getGamepads()[0]; | |
| if(firstGamepad) { | |
| pollGamepad(firstGamepad); | |
| } | |
| window.requestAnimationFrame(onTick); | |
| } | |
| window.requestAnimationFrame(onTick); | |
| const ONE_SECOND_AS_MILLISECONDS = 1000; | |
| const SAFETY_MODE_ACTIVATION_SECONDS = 60; | |
| const SAFETY_MODE_AUTOACTIVATE_TIME = ONE_SECOND_AS_MILLISECONDS * SAFETY_MODE_ACTIVATION_SECONDS; | |
| const KEYCODE_TAB = 9; | |
| const KEYCODE_NUMPAD_PLUS = 107; | |
| const KEYCODE_SHIFT = 16; | |
| const KEYCODE_NUMPAD_DECIMAL = 110; | |
| const SAFE_MODE_BACKGROUND_COLOR = "#ccffcc"; | |
| const DANGER_MODE_BACKGROUND_COLOR = "#ffcccc"; | |
| const RETURNABLE_TOGGLE_KEY = "u"; | |
| const NUMBERS_TOGGLE_KEY = "i"; | |
| const BOTTOM_ROW_LETTERS_TOGGLE_KEY = "o"; | |
| const AUTOSUBMIT_TOGGLE_KEY = "p"; | |
| const safetyModeInit = ( | |
| gidStatuses[gid] === 'alwayson' ? | |
| (false) | |
| : | |
| (GM_getValue('safetyMode')) | |
| ); | |
| document.body.insertAdjacentHTML( | |
| `afterbegin`, | |
| `<div id="keybindsDiv" tabindex="-1" style="background-color: ${safetyModeInit ? SAFE_MODE_BACKGROUND_COLOR : DANGER_MODE_BACKGROUND_COLOR }; font-size: 0.7em;">` + | |
| `<img src="data:image/jpg;base64,${SB_LOGO}" style="margin-right: 10px;">` + | |
| `<button id='permEnable'>+GID</button> ` + | |
| `<button id='permDisable'><strike>GID</strike></button>` + | |
| `<label tabindex="-1" id="safetyModeLabel" ${safetyModeInit ? `style="color:green;"` : `style="color:red";`}><span id="safetyModeStatus"> <span id="lock">${safetyModeInit ? `🔒` : `🔓`}</span> SAFETY TOGGLE:</span> ` + | |
| `<input tabindex="-1" type="checkbox" id="safetyMode" ${safetyModeInit ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; margin-left: 10px;">KEYBINDS (salembeats)</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">Auto Submit: ` + | |
| `<input tabindex="-1" id="autosubmit" type="checkbox" ${GM_getValue(`autosubmit`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">Use [z-.]: ` + | |
| `<input tabindex="-1" id="letters" type="checkbox" ${GM_getValue(`letters`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">Use [1-9]: ` + | |
| `<input tabindex="-1" id="numbers" type="checkbox" ${GM_getValue(`numbers`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">Return w/ Shift/Num+: ` + | |
| `<input tabindex="-1" id="returnable" type="checkbox" ${GM_getValue(`returnable`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">AutoNext: ` + | |
| `<input tabindex="-1" id="autoNext" type="checkbox" ${GM_getValue(`autoNext`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `<label tabindex="-1" style="color: black; float: right; margin-right: 10px;">Controller: ` + | |
| `<input tabindex="-1" id="useController" type="checkbox" ${GM_getValue(`useController`) ? `checked` : ``}></input>` + | |
| `</label>` + | |
| `</div>` | |
| ); | |
| const numbers = document.getElementById(`numbers`); | |
| const letters = document.getElementById(`letters`); | |
| const autosubmit = document.getElementById(`autosubmit`); | |
| const safetyMode = document.getElementById(`safetyMode`); | |
| const safetyModeLabel = document.getElementById('safetyModeLabel'); | |
| const safetyModeStatus = document.getElementById('safetyModeStatus'); | |
| const returnable = document.getElementById('returnable'); | |
| const autoNext = document.getElementById('autoNext'); | |
| const useController = document.getElementById('useController'); | |
| const permDisable = document.getElementById('permDisable'); | |
| const permEnable = document.getElementById('permEnable'); | |
| useController.addEventListener(`change`, function (event) { | |
| GM_setValue(`useController`, useController.checked); | |
| }); | |
| autoNext.addEventListener(`change`, function (event) { | |
| GM_setValue(`autoNext`, autoNext.checked); | |
| }); | |
| numbers.addEventListener(`change`, function (event) { | |
| GM_setValue(`numbers`, numbers.checked); | |
| }); | |
| letters.addEventListener(`change`, function (event) { | |
| GM_setValue(`letters`, letters.checked); | |
| }); | |
| autosubmit.addEventListener(`change`, function (event) { | |
| GM_setValue(`autosubmit`, autosubmit.checked); | |
| }); | |
| returnable.addEventListener(`change`, function (event) { | |
| GM_setValue(`returnable`, returnable.checked); | |
| }); | |
| permEnable.addEventListener('click', event => { | |
| if(confirm("This will bypass safety mode for this GID permanently.\nAre you sure?")) { | |
| gidStatuses[gid] = 'alwayson'; | |
| GM_setValue('gidStatuses', JSON.stringify(gidStatuses)); | |
| } | |
| }); | |
| permDisable.addEventListener('click', event => { | |
| if(confirm("This will disable this script for this GID permanently.\nThis includes the menu bar which allows you to change this setting.\nAre you sure?")) { | |
| gidStatuses[gid] = 'disabled'; | |
| GM_setValue('gidStatuses', JSON.stringify(gidStatuses)); | |
| } | |
| }); | |
| function scrollSelectedRadioGroupIntoView() { | |
| let centralRadio = selectedRadioGroup.radios[Math.floor(selectedRadioGroup.radios.length/2)]; | |
| centralRadio.scrollIntoView({block: "center", inline: "center"}); | |
| } | |
| function cycleRadioGroup() { | |
| unhighlightSelectedRadioGroup(); | |
| selectedRadioGroupIndex++; | |
| selectedRadioGroupIndex = selectedRadioGroupIndex % (radioGroups.length); | |
| selectedRadioGroup = radioGroups[selectedRadioGroupIndex]; | |
| scrollSelectedRadioGroupIntoView(); | |
| highlightSelectedRadioGroup(); | |
| } | |
| function doAllRadioGroupsHaveAResponse() { | |
| for(let radioGroup of radioGroups) { | |
| let {radios} = radioGroup; | |
| for(let [index, radio] of radios.entries()) { | |
| if(radio.checked) { break; } | |
| else if(index === radios.length - 1) { return false; } | |
| } | |
| } | |
| return true; | |
| } | |
| function clickCurrentRadioGroupButtonByIndex(index) { | |
| let radioToClick = selectedRadioGroup.radios[index]; | |
| if(radioToClick) { | |
| radioToClick.click(); | |
| if(autoNext.checked) { | |
| cycleRadioGroup(); | |
| } | |
| } | |
| else { | |
| console.log(`Radio at index ${index} does not exist.`); | |
| } | |
| } | |
| function clickRadioButtonByIndex(index) { | |
| let radioToClick = document.querySelectorAll(`[type="radio"]`)[index]; | |
| if(radioToClick) { | |
| radioToClick.click(); | |
| } | |
| else { | |
| console.log(`Radio at index ${index} does not exist.`); | |
| } | |
| } | |
| function setSafetyMode() { | |
| if(gidStatuses[gid] !== 'alwayson') { | |
| safetyMode.checked = true; | |
| GM_setValue('safetyMode', safetyMode.checked); | |
| safetyModeStatus.innerText = "🔒 (LOCKED) SAFETY TOGGLE: "; | |
| safetyModeStatus.style.color ="green"; | |
| keybindsDiv.style.backgroundColor = SAFE_MODE_BACKGROUND_COLOR; | |
| } | |
| } | |
| function defeatSafetyMode() { | |
| safetyMode.checked = false; | |
| GM_setValue('safetyMode', safetyMode.checked); | |
| safetyModeStatus.innerText = "⌨️ (UNLOCKED) SAFETY TOGGLE: "; | |
| safetyModeStatus.style.color = "red"; | |
| keybindsDiv.style.backgroundColor = DANGER_MODE_BACKGROUND_COLOR; | |
| } | |
| function isSafetyModeOn() { | |
| return (safetyMode.checked); | |
| } | |
| function isSafetyModeOff() { | |
| return !isSafetyModeOn(); | |
| } | |
| function areNumbersEnabled() { | |
| return numbers.checked; | |
| } | |
| function areLettersEnabled() { | |
| return letters.checked; | |
| } | |
| function isReturningEnabled() { | |
| return returnable.checked; | |
| } | |
| function isNumberKey(key) { | |
| return Boolean(key.match(/^[0-9]$/)); | |
| } | |
| function isBottomRowLetterKey(key) { | |
| return Boolean(key.match(/^[zxcvbnm,\.\/]$/)); | |
| } | |
| function submitHit() { | |
| (document.querySelector(`[type="submit"]`) || document.querySelector("#submit")).click(); | |
| GM_setValue('lastSubmitTime',new Date().getTime()); | |
| } | |
| safetyMode.addEventListener(`change`, function(event) { | |
| if(safetyMode.checked) { | |
| setSafetyMode(); | |
| } | |
| else | |
| { | |
| defeatSafetyMode(); | |
| } | |
| }); | |
| var lastSubmitTime = Number(GM_getValue('lastSubmitTime')); | |
| function shouldAutomaticallyEnableSafetyMode() { | |
| if(gidStatuses[gid] === 'alwayson') {return false;} | |
| return (new Date().getTime() - lastSubmitTime) > SAFETY_MODE_AUTOACTIVATE_TIME || | |
| (!GM_getValue('lastSubmitTime')); | |
| } | |
| if( shouldAutomaticallyEnableSafetyMode() ) { | |
| setSafetyMode(); | |
| safetyModeStatus.innerText = `🔒 (AUTO-LOCKED, > ${SAFETY_MODE_ACTIVATION_SECONDS}s) SAFETY TOGGLE: `; | |
| } | |
| else { | |
| if(isSafetyModeOff()) { | |
| scrollSelectedRadioGroupIntoView(); | |
| highlightSelectedRadioGroup(); | |
| } | |
| } | |
| var escapeCount = 0; | |
| document.addEventListener("click", e => { | |
| if (isSafetyModeOff() && autosubmit.checked && doAllRadioGroupsHaveAResponse()) { | |
| submitHit(); | |
| } | |
| }); | |
| window.addEventListener(`keydown`, function radioKeybindKeydown(event) { | |
| if(event.key.match(/Escape/)) | |
| { | |
| if(escapeCount < 2) { | |
| escapeCount++; | |
| } | |
| else { | |
| escapeCount = 0; | |
| if( isSafetyModeOn() ) { | |
| defeatSafetyMode(); | |
| scrollSelectedRadioGroupIntoView(); | |
| highlightSelectedRadioGroup(); | |
| } | |
| else { | |
| setSafetyMode(); | |
| } | |
| } | |
| } | |
| if( isSafetyModeOff() ) { | |
| const key = event.key; | |
| if (key.match(/Enter/)) { | |
| submitHit(); | |
| } | |
| switch(event.key) { | |
| case RETURNABLE_TOGGLE_KEY: | |
| returnable.checked = !returnable.checked; | |
| break; | |
| case NUMBERS_TOGGLE_KEY: | |
| numbers.checked = !numbers.checked; | |
| break; | |
| case BOTTOM_ROW_LETTERS_TOGGLE_KEY: | |
| letters.checked = !letters.checked; | |
| break; | |
| case AUTOSUBMIT_TOGGLE_KEY: | |
| autosubmit.checked = !autosubmit.checked; | |
| break; | |
| default: | |
| break; | |
| } | |
| if( event.keyCode === KEYCODE_TAB || event.keyCode === KEYCODE_NUMPAD_DECIMAL) { | |
| event.preventDefault(); | |
| cycleRadioGroup(); | |
| } | |
| if( isReturningEnabled() ) { | |
| if(event.keyCode === KEYCODE_NUMPAD_PLUS || | |
| event.keyCode === KEYCODE_SHIFT) { | |
| event.preventDefault(); | |
| mTurkParentWindow.returnHIT(); | |
| } | |
| } | |
| if ( areNumbersEnabled() && isNumberKey(key) ) { | |
| const numberKeyToIndex = { '1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '8': 7, '9': 8 , '0': 9}; | |
| clickCurrentRadioGroupButtonByIndex(numberKeyToIndex[key]); | |
| if (autosubmit.checked && doAllRadioGroupsHaveAResponse()) { | |
| submitHit(); | |
| } | |
| } | |
| if ( areLettersEnabled() && isBottomRowLetterKey(key) ) { | |
| const letterKeyToIndex = { 'z': 0, 'x': 1, 'c': 2, 'v': 3, 'b': 4, 'n': 5, 'm': 6, ',': 7, '.': 8, '/': 9 }; | |
| clickCurrentRadioGroupButtonByIndex(letterKeyToIndex[key]); | |
| if (autosubmit.checked && doAllRadioGroupsHaveAResponse()) { | |
| submitHit(); | |
| } | |
| } | |
| } | |
| }); | |
| window.focus(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment