|
|
|
@ -74,9 +74,9 @@ const formFieldEntries = {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles typing in the values for the input form, dispatching each character
|
|
|
|
|
* as an event. This function assumes that all characters in the input will be
|
|
|
|
|
* UTF-8.
|
|
|
|
|
* Handles typing in the values for the input form. All values are written
|
|
|
|
|
* immediately, even though that would be physically impossible with a real
|
|
|
|
|
* keyboard.
|
|
|
|
|
*
|
|
|
|
|
* Note: this code will never break, but you might get warnings in the console
|
|
|
|
|
* from Angular about unexpected value changes. Angular patches over a lot of
|
|
|
|
@ -94,75 +94,32 @@ const formFieldEntries = {
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
function setInputValue(inputField, inputText) {
|
|
|
|
|
const continueEventName = "coder-patch--continue";
|
|
|
|
|
|
|
|
|
|
const keyboardInputPromise = /** @type {Promise<void>} */ (
|
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
|
if (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 = () => {
|
|
|
|
|
resolve();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -1 indicates a "pre-write" for clearing out the input before trying to
|
|
|
|
|
// write new text to it
|
|
|
|
|
let i = -1;
|
|
|
|
|
window.clearTimeout(timeoutId);
|
|
|
|
|
inputField.removeEventListener("input", handleSuccessfulDispatch);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// requestAnimationFrame is not capable of giving back values of 0 for its
|
|
|
|
|
// task IDs. Good default value to ensure that we don't need if statements
|
|
|
|
|
// when trying to cancel anything
|
|
|
|
|
let currentAnimationId = 0;
|
|
|
|
|
inputField.addEventListener("input", handleSuccessfulDispatch);
|
|
|
|
|
|
|
|
|
|
// Super easy to pool the same event objects, because the events don't
|
|
|
|
|
// have any custom, context-specific values on them, and they're
|
|
|
|
|
// restricted to this one callback.
|
|
|
|
|
const continueEvent = new CustomEvent(continueEventName);
|
|
|
|
|
// 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,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** @returns {void} */
|
|
|
|
|
const handleNextCharIndex = () => {
|
|
|
|
|
if (i === inputText.length) {
|
|
|
|
|
resolve();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const currentChar = inputText[i];
|
|
|
|
|
if (i !== -1 && currentChar === undefined) {
|
|
|
|
|
throw new Error("Went out of bounds");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
inputField.addEventListener(
|
|
|
|
|
continueEventName,
|
|
|
|
|
() => {
|
|
|
|
|
i++;
|
|
|
|
|
currentAnimationId =
|
|
|
|
|
window.requestAnimationFrame(handleNextCharIndex);
|
|
|
|
|
},
|
|
|
|
|
{ once: true },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (i === -1) {
|
|
|
|
|
inputField.value = "";
|
|
|
|
|
} else {
|
|
|
|
|
inputField.value = inputField.value + currentChar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inputField.value = inputText;
|
|
|
|
|
inputField.dispatchEvent(inputEvent);
|
|
|
|
|
inputField.dispatchEvent(continueEvent);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
cancelAnimationFrame(currentAnimationId);
|
|
|
|
|
reject(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
currentAnimationId = window.requestAnimationFrame(handleNextCharIndex);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return keyboardInputPromise;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|