From 01a25f7182c4ee82a0e6f7e63ec97243c7b59452 Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Tue, 22 Oct 2024 11:41:32 +0500 Subject: [PATCH] Refactor devolutions-patch.js for reduced indent size Refactor the `windows-rdp/devolutions-patch.js` script to use a two-space indentation style from a previous four-space style. This change improves code readability and consistency with the project's codebase formatting style. All functional code remains unaffected; only formatting has been adjusted. --- windows-rdp/devolutions-patch.js | 490 +++++++++++++++---------------- 1 file changed, 245 insertions(+), 245 deletions(-) diff --git a/windows-rdp/devolutions-patch.js b/windows-rdp/devolutions-patch.js index 43577b1..020a40f 100644 --- a/windows-rdp/devolutions-patch.js +++ b/windows-rdp/devolutions-patch.js @@ -53,23 +53,23 @@ const SCREEN_POLL_INTERVAL_MS = 500; * @satisfies {FormFieldEntries} */ const formFieldEntries = { - /** @readonly */ - username: { - /** @readonly */ - querySelector: "web-client-username-control input", - - /** @readonly */ - value: "${CODER_USERNAME}", - }, - - /** @readonly */ - password: { - /** @readonly */ - querySelector: "web-client-password-control input", - - /** @readonly */ - value: "${CODER_PASSWORD}", - }, + /** @readonly */ + username: { + /** @readonly */ + querySelector: "web-client-username-control input", + + /** @readonly */ + value: "${CODER_USERNAME}", + }, + + /** @readonly */ + password: { + /** @readonly */ + querySelector: "web-client-password-control input", + + /** @readonly */ + value: "${CODER_PASSWORD}", + }, }; /** @@ -93,32 +93,32 @@ const formFieldEntries = { * @returns {Promise} */ function setInputValue(inputField, inputText) { - return new Promise((resolve, reject) => { - // Adding timeout for input event, even though we'll be dispatching it - // immediately, just in the off chance that something in the Angular app - // intercepts it or stops it from propagating properly - const timeoutId = window.setTimeout(() => { - reject(new Error("Input event did not get processed correctly in time.")); - }, 3_000); - - const handleSuccessfulDispatch = () => { - window.clearTimeout(timeoutId); - inputField.removeEventListener("input", handleSuccessfulDispatch); - resolve(); - }; - - inputField.addEventListener("input", handleSuccessfulDispatch); - - // Code assumes that Angular will have an event handler in place to handle - // the new event - const inputEvent = new Event("input", { - bubbles: true, - cancelable: true, - }); - - inputField.value = inputText; - inputField.dispatchEvent(inputEvent); - }); + return new Promise((resolve, reject) => { + // Adding timeout for input event, even though we'll be dispatching it + // immediately, just in the off chance that something in the Angular app + // intercepts it or stops it from propagating properly + const timeoutId = window.setTimeout(() => { + reject(new Error("Input event did not get processed correctly in time.")); + }, 3_000); + + const handleSuccessfulDispatch = () => { + window.clearTimeout(timeoutId); + inputField.removeEventListener("input", handleSuccessfulDispatch); + resolve(); + }; + + inputField.addEventListener("input", handleSuccessfulDispatch); + + // Code assumes that Angular will have an event handler in place to handle + // the new event + const inputEvent = new Event("input", { + bubbles: true, + cancelable: true, + }); + + inputField.value = inputText; + inputField.dispatchEvent(inputEvent); + }); } /** @@ -137,91 +137,91 @@ function setInputValue(inputField, inputText) { * @returns {Promise} */ async function autoSubmitForm(myForm) { - const setProtocolValue = () => { - /** @type {HTMLDivElement | null} */ - const protocolDropdownTrigger = myForm.querySelector('div[role="button"]'); - if (protocolDropdownTrigger === null) { - throw new Error("No clickable trigger for setting protocol value"); - } - - protocolDropdownTrigger.click(); - - // Can't use form as container for querying the list of dropdown options, - // because the elements don't actually exist inside the form. They're placed - // in the top level of the HTML doc, and repositioned to make it look like - // they're part of the form. Avoids CSS stacking context issues, maybe? - /** @type {HTMLLIElement | null} */ - const protocolOption = document.querySelector( - `p-dropdownitem[ng-reflect-label="${PROTOCOL}"] li`, - ); - - if (protocolOption === null) { - throw new Error( - "Unable to find protocol option on screen that matches desired protocol", - ); - } - - protocolOption.click(); - }; - - const setHostname = () => { - /** @type {HTMLInputElement | null} */ - const hostnameInput = myForm.querySelector("p-autocomplete#hostname input"); - - if (hostnameInput === null) { - throw new Error("Unable to find field for adding hostname"); - } - - return setInputValue(hostnameInput, HOSTNAME); - }; - - const setCoderFormFieldValues = async () => { - // The RDP form will not appear on screen unless the dropdown is set to use - // the RDP protocol - const rdpSubsection = myForm.querySelector("rdp-form"); - if (rdpSubsection === null) { - throw new Error( - "Unable to find RDP subsection. Is the value of the protocol set to RDP?", - ); - } - - for (const { value, querySelector } of Object.values(formFieldEntries)) { - /** @type {HTMLInputElement | null} */ - const input = document.querySelector(querySelector); - - if (input === null) { - throw new Error( - `Unable to element that matches query "${querySelector}"`, - ); - } - - await setInputValue(input, value); - } - }; - - const triggerSubmission = () => { - /** @type {HTMLButtonElement | null} */ - const submitButton = myForm.querySelector( - 'p-button[ng-reflect-type="submit"] button', - ); - - if (submitButton === null) { - throw new Error("Unable to find submission button"); - } - - if (submitButton.disabled) { - throw new Error( - "Unable to submit form because submit button is disabled. Are all fields filled out correctly?", - ); - } - - submitButton.click(); - }; - - setProtocolValue(); - await setHostname(); - await setCoderFormFieldValues(); - triggerSubmission(); + const setProtocolValue = () => { + /** @type {HTMLDivElement | null} */ + const protocolDropdownTrigger = myForm.querySelector('div[role="button"]'); + if (protocolDropdownTrigger === null) { + throw new Error("No clickable trigger for setting protocol value"); + } + + protocolDropdownTrigger.click(); + + // Can't use form as container for querying the list of dropdown options, + // because the elements don't actually exist inside the form. They're placed + // in the top level of the HTML doc, and repositioned to make it look like + // they're part of the form. Avoids CSS stacking context issues, maybe? + /** @type {HTMLLIElement | null} */ + const protocolOption = document.querySelector( + 'p-dropdownitem[ng-reflect-label="' + PROTOCOL + '"] li', + ); + + if (protocolOption === null) { + throw new Error( + "Unable to find protocol option on screen that matches desired protocol", + ); + } + + protocolOption.click(); + }; + + const setHostname = () => { + /** @type {HTMLInputElement | null} */ + const hostnameInput = myForm.querySelector("p-autocomplete#hostname input"); + + if (hostnameInput === null) { + throw new Error("Unable to find field for adding hostname"); + } + + return setInputValue(hostnameInput, HOSTNAME); + }; + + const setCoderFormFieldValues = async () => { + // The RDP form will not appear on screen unless the dropdown is set to use + // the RDP protocol + const rdpSubsection = myForm.querySelector("rdp-form"); + if (rdpSubsection === null) { + throw new Error( + "Unable to find RDP subsection. Is the value of the protocol set to RDP?", + ); + } + + for (const { value, querySelector } of Object.values(formFieldEntries)) { + /** @type {HTMLInputElement | null} */ + const input = document.querySelector(querySelector); + + if (input === null) { + throw new Error( + 'Unable to element that matches query "' + querySelector + '"', + ); + } + + await setInputValue(input, value); + } + }; + + const triggerSubmission = () => { + /** @type {HTMLButtonElement | null} */ + const submitButton = myForm.querySelector( + 'p-button[ng-reflect-type="submit"] button', + ); + + if (submitButton === null) { + throw new Error("Unable to find submission button"); + } + + if (submitButton.disabled) { + throw new Error( + "Unable to submit form because submit button is disabled. Are all fields filled out correctly?", + ); + } + + submitButton.click(); + }; + + setProtocolValue(); + await setHostname(); + await setCoderFormFieldValues(); + triggerSubmission(); } /** @@ -231,58 +231,58 @@ async function autoSubmitForm(myForm) { * @returns {void} */ function setupFormDetection() { - /** @type {HTMLFormElement | null} */ - let formValueFromLastMutation = null; - - /** @returns {void} */ - const onDynamicTabMutation = () => { - /** @type {HTMLFormElement | null} */ - const latestForm = document.querySelector("web-client-form > form"); - - // Only try to auto-fill if we went from having no form on screen to - // having a form on screen. That way, we don't accidentally override the - // form if the user is trying to customize values, and this essentially - // makes the script values function as default values - const mounted = formValueFromLastMutation === null && latestForm !== null; - if (mounted) { - autoSubmitForm(latestForm); - } - - formValueFromLastMutation = latestForm; - }; - - /** @type {number | undefined} */ - let pollingId = undefined; - - /** @returns {void} */ - const checkScreenForDynamicTab = () => { - const dynamicTab = document.querySelector("web-client-dynamic-tab"); - - // Keep polling until the main content container is on screen - if (dynamicTab === null) { - return; - } - - window.clearInterval(pollingId); - - // Call the mutation callback manually, to ensure it runs at least once - onDynamicTabMutation(); - - // Having the mutation observer is kind of an extra safety net that isn't - // really expected to run that often. Most of the content in the dynamic - // tab is being rendered through Canvas, which won't trigger any mutations - // that the observer can detect - const dynamicTabObserver = new MutationObserver(onDynamicTabMutation); - dynamicTabObserver.observe(dynamicTab, { - subtree: true, - childList: true, - }); - }; - - pollingId = window.setInterval( - checkScreenForDynamicTab, - SCREEN_POLL_INTERVAL_MS, - ); + /** @type {HTMLFormElement | null} */ + let formValueFromLastMutation = null; + + /** @returns {void} */ + const onDynamicTabMutation = () => { + /** @type {HTMLFormElement | null} */ + const latestForm = document.querySelector("web-client-form > form"); + + // Only try to auto-fill if we went from having no form on screen to + // having a form on screen. That way, we don't accidentally override the + // form if the user is trying to customize values, and this essentially + // makes the script values function as default values + const mounted = formValueFromLastMutation === null && latestForm !== null; + if (mounted) { + autoSubmitForm(latestForm); + } + + formValueFromLastMutation = latestForm; + }; + + /** @type {number | undefined} */ + let pollingId = undefined; + + /** @returns {void} */ + const checkScreenForDynamicTab = () => { + const dynamicTab = document.querySelector("web-client-dynamic-tab"); + + // Keep polling until the main content container is on screen + if (dynamicTab === null) { + return; + } + + window.clearInterval(pollingId); + + // Call the mutation callback manually, to ensure it runs at least once + onDynamicTabMutation(); + + // Having the mutation observer is kind of an extra safety net that isn't + // really expected to run that often. Most of the content in the dynamic + // tab is being rendered through Canvas, which won't trigger any mutations + // that the observer can detect + const dynamicTabObserver = new MutationObserver(onDynamicTabMutation); + dynamicTabObserver.observe(dynamicTab, { + subtree: true, + childList: true, + }); + }; + + pollingId = window.setInterval( + checkScreenForDynamicTab, + SCREEN_POLL_INTERVAL_MS, + ); } /** @@ -292,34 +292,34 @@ function setupFormDetection() { * @returns {void} */ function setupAlwaysOnStyles() { - const styleId = "coder-patch--styles-always-on"; - const existingContainer = document.querySelector(`#${styleId}`); - if (existingContainer) { - return; - } - - const styleContainer = document.createElement("style"); - styleContainer.id = styleId; - styleContainer.innerHTML = ` + const styleId = "coder-patch--styles-always-on"; + const existingContainer = document.querySelector("#" + styleId); + if (existingContainer) { + return; + } + + const styleContainer = document.createElement("style"); + styleContainer.id = styleId; + styleContainer.innerHTML = ` /* app-menu corresponds to the sidebar of the default view. */ app-menu { display: none !important; } `; - document.head.appendChild(styleContainer); + document.head.appendChild(styleContainer); } function hideFormForInitialSubmission() { - const styleId = "coder-patch--styles-initial-submission"; - const cssOpacityVariableName = "--coder-opacity-multiplier"; - - /** @type {HTMLStyleElement | null} */ - let styleContainer = document.querySelector(`#${styleId}`); - if (!styleContainer) { - styleContainer = document.createElement("style"); - styleContainer.id = styleId; - styleContainer.innerHTML = ` + const styleId = "coder-patch--styles-initial-submission"; + const cssOpacityVariableName = "--coder-opacity-multiplier"; + + /** @type {HTMLStyleElement | null} */ + let styleContainer = document.querySelector("#" + styleId); + if (!styleContainer) { + styleContainer = document.createElement("style"); + styleContainer.id = styleId; + styleContainer.innerHTML = ` /* Have to use opacity instead of visibility, because the element still needs to be interactive via the script so that it can be auto-filled. @@ -350,50 +350,50 @@ function hideFormForInitialSubmission() { } `; - document.head.appendChild(styleContainer); - } - - // The root node being undefined should be physically impossible (if it's - // undefined, the browser itself is busted), but we need to do a type check - // here so that the rest of the function doesn't need to do type checks over - // and over. - const rootNode = document.querySelector(":root"); - if (!(rootNode instanceof HTMLHtmlElement)) { - // Remove the container entirely because if the browser is busted, who knows - // if the CSS variables can be applied correctly. Better to have something - // be a bit more ugly/painful to use, than have it be impossible to use - styleContainer.remove(); - return; - } - - // It's safe to make the form visible preemptively because Devolutions - // outputs the Windows view through an HTML canvas that it overlays on top - // of the rest of the app. Even if the form isn't hidden at the style level, - // it will still be covered up. - const restoreOpacity = () => { - rootNode.style.setProperty(cssOpacityVariableName, "1"); - }; - - // If this file gets more complicated, it might make sense to set up the - // timeout and event listener so that if one triggers, it cancels the other, - // but having restoreOpacity run more than once is a no-op for right now. - // Not a big deal if these don't get cleaned up. - - // Have the form automatically reappear no matter what, so that if something - // does break, the user isn't left out to dry - window.setTimeout(restoreOpacity, 5_000); - - /** @type {HTMLFormElement | null} */ - const form = document.querySelector("web-client-form > form"); - form?.addEventListener( - "submit", - () => { - // Not restoring opacity right away just to give the HTML canvas a little - // bit of time to get spun up and cover up the main form - window.setTimeout(restoreOpacity, 1_000); - }, - { once: true }, - ); + document.head.appendChild(styleContainer); + } + + // The root node being undefined should be physically impossible (if it's + // undefined, the browser itself is busted), but we need to do a type check + // here so that the rest of the function doesn't need to do type checks over + // and over. + const rootNode = document.querySelector(":root"); + if (!(rootNode instanceof HTMLHtmlElement)) { + // Remove the container entirely because if the browser is busted, who knows + // if the CSS variables can be applied correctly. Better to have something + // be a bit more ugly/painful to use, than have it be impossible to use + styleContainer.remove(); + return; + } + + // It's safe to make the form visible preemptively because Devolutions + // outputs the Windows view through an HTML canvas that it overlays on top + // of the rest of the app. Even if the form isn't hidden at the style level, + // it will still be covered up. + const restoreOpacity = () => { + rootNode.style.setProperty(cssOpacityVariableName, "1"); + }; + + // If this file gets more complicated, it might make sense to set up the + // timeout and event listener so that if one triggers, it cancels the other, + // but having restoreOpacity run more than once is a no-op for right now. + // Not a big deal if these don't get cleaned up. + + // Have the form automatically reappear no matter what, so that if something + // does break, the user isn't left out to dry + window.setTimeout(restoreOpacity, 5_000); + + /** @type {HTMLFormElement | null} */ + const form = document.querySelector("web-client-form > form"); + form?.addEventListener( + "submit", + () => { + // Not restoring opacity right away just to give the HTML canvas a little + // bit of time to get spun up and cover up the main form + window.setTimeout(restoreOpacity, 1_000); + }, + { once: true }, + ); } // Always safe to call these immediately because even if the Angular app isn't @@ -403,7 +403,7 @@ setupAlwaysOnStyles(); hideFormForInitialSubmission(); if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", setupFormDetection); + document.addEventListener("DOMContentLoaded", setupFormDetection); } else { - setupFormDetection(); + setupFormDetection(); }