/* autogenerated output of all executables in the /scouting/executables folder */ var executables = {} //climbHighlight.js executables["climbHighlight"] = { execute(button, layers, args) { const otherClimbButtons = layers .flat() .filter((x) => x.executables.find((e) => e.type == "climbHighlight")); console.log(otherClimbButtons); for (let otherButton of otherClimbButtons) { otherButton.element.classList.remove("highlight"); } button.element.classList.add("highlight"); }, async reverse(button, layers, args) { //find the current climb position by traversing the action queue backwards let highlightableButtons = {}; for (let layerButton of layers.flat()) { if (layerButton.executables.find((e) => e.type == "climbHighlight")) //if its highlightable highlightableButtons[layerButton.id] = layerButton; //add its id } let oldHighlightId = [...actionQueue] .reverse() .find((x) => x && x.id in highlightableButtons).id; //re add highlights button.element.classList.remove("highlight"); highlightableButtons[oldHighlightId].element.classList.add("highlight"); }, }; //conditionalLayer.js /** * Shows another layer on button press * @param fromLayer the layer number the executable is transitioning from (normally the layer which contains the button) * @param toLayer the layer which the executable is transitioning to (the layer that will be shown). */ executables["conditionalLayerUndo"] = { execute(button, layers, fromLayer, toLayer, alwaysRender, conditionalRender) { for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } console.log( "CONDITIONAL LAYER(UNDO) --------------------------------------" ); console.log("variables"); console.log(variables); var renderedButtons = []; for (let button of layers[toLayer]) { var targetVariables = []; var targetValues = []; var thingsToCheck = {}; for (let [variable, valueData] of Object.entries(conditionalRender)) { for (let [value, idList] of Object.entries(valueData)) { if (idList.includes(button.id)) { if (thingsToCheck[variable]) { thingsToCheck[variable].push(value); } else { thingsToCheck[variable] = [value]; } } } } var display = false; for (let [variable, values] of Object.entries(thingsToCheck)) { if (values.includes(variables[variable].current)) { display = true; } } if (alwaysRender.includes(button.id) || display) { button.element.style.display = "flex"; renderedButtons.push(button); } } previousLayers.push(renderedButtons); }, reverse(button, layers, fromLayer, toLayer, alwaysRender, conditionalRender) { console.log("undoing conditional layer"); for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } previousLayers.pop(); if (previousLayers.length > 1) { previousLayers.pop(); } if (previousLayers.length - 1 > 0) { for (let button of previousLayers[previousLayers.length - 1]) { button.element.style.display = "flex"; console.log(`displaying ${button.id}`); } } }, }; //conditionalLayerOnUndo.js /** * Shows another layer on button press * @param fromLayer the layer number the executable is transitioning from (normally the layer which contains the button) * @param toLayer the layer which the executable is transitioning to (the layer that will be shown). */ executables["conditionalLayer"] = { execute(button, layers, fromLayer, toLayer, alwaysRender, conditionalRender) { for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } console.log("CONDITIONAL LAYER --------------------------------------"); console.log("variables"); console.log(variables); var renderedButtons = []; for (let button of layers[toLayer]) { var targetVariables = []; var targetValues = []; var thingsToCheck = {}; for (let [variable, valueData] of Object.entries(conditionalRender)) { for (let [value, idList] of Object.entries(valueData)) { if (idList.includes(button.id)) { if (thingsToCheck[variable]) { thingsToCheck[variable].push(value); } else { thingsToCheck[variable] = [value]; } } } } var display = false; for (let [variable, values] of Object.entries(thingsToCheck)) { if (values.includes(variables[variable].current)) { display = true; } } if (alwaysRender.includes(button.id) || display) { button.element.style.display = "flex"; renderedButtons.push(button); } } previousLayers.push(renderedButtons); }, reverse(button, layers, fromLayer, toLayer, alwaysRender, conditionalRender) { console.log("undoing conditional layer"); for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } previousLayers.pop(); if (previousLayers.length > 1) { previousLayers.pop(); } if (previousLayers.length - 1 > 0) { for (let button of previousLayers[previousLayers.length - 1]) { button.element.style.display = "flex"; console.log(`displaying ${button.id}`); } } }, }; //constantPosition.js executables["constantPosition"] = { execute(button, layers, position) { //add the constant position to the action if (!actionQueue[actionQueue.length - 1].other) actionQueue[actionQueue.length - 1].other = {}; actionQueue[actionQueue.length - 1].other.pos = position; }, reverse(button, layers, args) { //nothing to undo }, }; //exampleExecutable.js executables["example"] = { execute(button, layers, args) { //when the button is pressed, do this }, reverse(button, layers, args) { //when the button is undone, do this. This should undo EVERYTHING done by execute }, }; //flashBorder.js executables["flashBorder"] = { execute(button, layers, args) { //when the button is pressed, do this }, reverse(button, layers, args) { //when the button is undone, do this. This should undo EVERYTHING done by execute }, }; //hide.js /** * Hides the button on execute */ executables["hide"] = { execute(button) { //when the button is pressed, do this button.element.style.display = "none"; }, reverse(button) { //when the button is undone, do this. This should undo EVERYTHING done by execute button.element.style.display = "flex"; }, }; //layer.js /** * Shows another layer on button press * @param fromLayer the layer number the executable is transitioning from (normally the layer which contains the button) * @param toLayer the layer which the executable is transitioning to (the layer that will be shown). */ executables["layer"] = { execute(button, layers, fromLayer, toLayer) { for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } var renderedButtons = []; for (let button of layers[toLayer]) { button.element.style.display = "flex"; renderedButtons.push(button); } previousLayers.push(renderedButtons); }, reverse(button, layers, fromLayer, toLayer) { for (let button of layers.flat()) { //hide all buttons button.element.style.display = "none"; } previousLayers.pop(); console.log(previousLayers); for (let button of previousLayers[previousLayers.length - 1]) { button.element.style.display = "flex"; } }, }; //multiplier.js executables["multiplier"] = { execute(button, layers, numActions) { if (button.type != "action") { console.warn( "The 'multiplier' executable should only be used with 'action' buttons! Using it with buttons of other types can lead to unexpected results." ); } //add numActions-1 actions to the action queue for (let i = 0; i < numActions - 1; i++) { actionQueue.push({ id: button.id, ts: actionQueue[actionQueue.length - 1].ts, //get the time from the last action (the one created by the initial button press) }); } }, reverse(button, layers, numActions) { //add numActions-1 actions to the action queue for (let i = 0; i < numActions - 1; i++) { actionQueue.pop(); } }, }; //position.js /* * when the button is pressed, this executable collects position data from the user using a field map (src/scouting/public/img/field.png). * That data is then attached to the action in the "other.pos" field as percentages of an axis length (eg. action.other.pos = {x:0,y:0}) */ let positionConfig = { POSITION_LOCK_DELAY_MS: 1000, LOCK_BORDER_COLOR: "#00ffa5", }; let lockedPosition; let lockedTime; executables["position"] = { execute(button, layers) { //position lock check if (lockedTime + positionConfig.POSITION_LOCK_DELAY_MS > Date.now()) { // if there's a position in lockedPosition, put it in the action. if (!actionQueue[actionQueue.length - 1].other) actionQueue[actionQueue.length - 1].other = {}; actionQueue[actionQueue.length - 1].other.pos = lockedPosition; //set the border color and position lock time setAllButtonBorders(layers, positionConfig.LOCK_BORDER_COLOR); setTimeout(() => { if ( lockedTime + positionConfig.POSITION_LOCK_DELAY_MS <= Date.now() + 10 ) setAllButtonBorders(layers, "transparent"); }, positionConfig.POSITION_LOCK_DELAY_MS); lockedTime = Date.now(); return; } //when the button is pressed, do this const positionContainer = document.createElement("div"); positionContainer.style.cssText = ` width: 100vw; height: 100vh; position: absolute; top: 0; left: 0; display: flex; justify-content: center; align-items: center; background-color: white; `; const positionImage = new Image(); positionImage.src = "/img/field.svg"; positionImage.style.cssText = ` width: 100%; height: 100%; object-fit: contain; background-color: black; `; positionContainer.appendChild(positionImage); document.body.appendChild(positionContainer); positionImage.addEventListener("click", (e) => { /* position gathering */ //find image width/height and margin offsets let aspectRatio = positionImage.naturalWidth / positionImage.naturalHeight; let width = positionImage.height * aspectRatio; let height = positionImage.height; if (width > positionImage.width) { width = positionImage.width; height = positionImage.width / aspectRatio; } let marginX = (window.innerWidth - width) / 2; let marginY = (window.innerHeight - height) / 2; //find click/tap coordinates in terms of % of x and % of y let x = ((e.offsetX - marginX) / width) * 100; let y = ((e.offsetY - marginY) / height) * 100; //make x and y an integer between 0 and 100 x = Math.round(Math.max(Math.min(x, 100), 0)); y = Math.round(Math.max(Math.min(y, 100), 0)); //add the pos to the last action of the action queue (SHOULD be the action from the button that triggered this) if (!actionQueue[actionQueue.length - 1].other) actionQueue[actionQueue.length - 1].other = {}; let pos = (actionQueue[actionQueue.length - 1].other.pos = { x, y }); /* position lock */ if (positionConfig.POSITION_LOCK_DELAY_MS != 0) { //position lock is enabled, lockedTime = Date.now(); lockedPosition = pos; setAllButtonBorders(layers, positionConfig.LOCK_BORDER_COLOR); setTimeout(() => { if ( lockedTime + positionConfig.POSITION_LOCK_DELAY_MS <= Date.now() + 10 ) setAllButtonBorders(layers, "transparent"); }, positionConfig.POSITION_LOCK_DELAY_MS); } //remove the position image + container from DOM. document.body.removeChild(positionContainer); }); }, reverse(button, layers) { //nothing to do on reverse, but the function needs to be there }, }; function setAllButtonBorders(layers, color) { for (let button of layers.flat()) { if (button.executables.filter((x) => x.type == "position").length > 0) { //it has a position executable button.element.style.border = `0.5vw solid ${color}`; } } } //setVariable.js executables["setVariable"] = { execute(button, layers, name, value) { console.log("SET VARIABLE --------------------------------------"); if (!variables[name]) { variables[name] = { current: value, previous: [], }; } else { variables[name].previous.push(variables[name].current); variables[name].current = value; } console.log(variables); }, reverse(button, layers, name, value) { variables[name].current = variables[name].previous.pop(); console.log(`reversing ${name} to ${variables[name].current}`); }, };