mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-07 19:38:09 +08:00
refactor: custom text logic and modal (#5327)
This commit is contained in:
parent
727ff93611
commit
ccc9a39a99
26 changed files with 706 additions and 578 deletions
|
|
@ -23,10 +23,12 @@ const RESULT_SCHEMA = joi
|
|||
consistency: joi.number().min(0).max(100).required(),
|
||||
customText: joi.object({
|
||||
textLen: joi.number().required(),
|
||||
isWordRandom: joi.boolean().required(),
|
||||
isTimeRandom: joi.boolean().required(),
|
||||
word: joi.number().allow(null),
|
||||
time: joi.number().allow(null),
|
||||
mode: joi.string().valid("repeat", "random").required(),
|
||||
pipeDelimiter: joi.boolean().required(),
|
||||
limit: joi.object({
|
||||
mode: joi.string().valid("word", "time", "section").required(),
|
||||
value: joi.number().min(0).required(),
|
||||
}),
|
||||
}),
|
||||
difficulty: joi.string().valid("normal", "expert", "master").required(),
|
||||
funbox: joi
|
||||
|
|
|
|||
|
|
@ -84,20 +84,16 @@ export function isTestTooShort(result: SharedTypes.CompletedEvent): boolean {
|
|||
|
||||
if (mode === "custom") {
|
||||
if (!customText) return true;
|
||||
const { isWordRandom, isTimeRandom, textLen, word, time } = customText;
|
||||
const setTextTooShort =
|
||||
!isWordRandom && !isTimeRandom && _.isNumber(textLen) && textLen < 10;
|
||||
const randomWordsTooShort = isWordRandom && !isTimeRandom && word < 10;
|
||||
const randomTimeTooShort = !isWordRandom && isTimeRandom && time < 15;
|
||||
const wordLimitTooShort =
|
||||
(customText.limit.mode === "word" ||
|
||||
customText.limit.mode === "section") &&
|
||||
customText.limit.value < 10;
|
||||
const timeLimitTooShort =
|
||||
customText.limit.mode === "time" && customText.limit.value < 15;
|
||||
const bailedOutTooShort = bailedOut
|
||||
? bailedOut && testDuration < 15
|
||||
: false;
|
||||
return (
|
||||
setTextTooShort ||
|
||||
randomWordsTooShort ||
|
||||
randomTimeTooShort ||
|
||||
bailedOutTooShort
|
||||
);
|
||||
return wordLimitTooShort || timeLimitTooShort || bailedOutTooShort;
|
||||
}
|
||||
|
||||
if (mode === "zen") {
|
||||
|
|
|
|||
|
|
@ -372,20 +372,20 @@
|
|||
<div class="buttonsTop">
|
||||
<div class="button saveCustomText">
|
||||
<i class="fas fa-save"></i>
|
||||
Save
|
||||
save
|
||||
</div>
|
||||
<div class="button showSavedTexts">
|
||||
<i class="fas fa-folder"></i>
|
||||
Show saved texts
|
||||
saved texts
|
||||
</div>
|
||||
<input id="fileInput" type="file" class="hidden" accept=".txt" />
|
||||
<label for="fileInput" class="button importText">
|
||||
<i class="fas fa-file-import"></i>
|
||||
Open file
|
||||
open file
|
||||
</label>
|
||||
<div class="button wordfilter">
|
||||
<i class="fas fa-filter"></i>
|
||||
Words filter
|
||||
words filter
|
||||
</div>
|
||||
</div>
|
||||
<div class="savedTexts hidden" style="display: none">
|
||||
|
|
@ -405,70 +405,133 @@
|
|||
<p class="small">Click anywhere to start editing the text.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="challengeWarning">
|
||||
<div>
|
||||
<p>
|
||||
A challenge is currently loaded.
|
||||
<br />
|
||||
Editing the settings will clear the challenge.
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
<p class="small">Click anywhere to edit.</p>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="textarea"></textarea>
|
||||
</div>
|
||||
<div class="inputs">
|
||||
<label class="checkboxWithSub randomWordsCheckbox">
|
||||
<input type="checkbox" />
|
||||
<div>Random</div>
|
||||
<div class="sub">
|
||||
Randomize the above words, and control how many words are generated.
|
||||
<div class="group" data-id="mode">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-cog"></i>
|
||||
Mode
|
||||
</div>
|
||||
<div class="sub">Change the way words are generated.</div>
|
||||
<div class="groupInputs">
|
||||
<div class="buttonGroup">
|
||||
<button value="simple">simple</button>
|
||||
<button value="repeat">repeat</button>
|
||||
<button value="random">random</button>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div class="randomInputFields disabled">
|
||||
<label class="wordcount">
|
||||
Words
|
||||
<input type="number" value="" min="1" max="10000" />
|
||||
</label>
|
||||
<label class="sectioncount hidden">
|
||||
Sections
|
||||
<input type="number" value="" min="1" max="10000" />
|
||||
</label>
|
||||
<div style="color: var(--sub-color)">or</div>
|
||||
<label class="time">
|
||||
Time
|
||||
<input type="number" value="" min="1" max="10000" />
|
||||
</label>
|
||||
<!-- <div style="color: var(--sub-color)">or</div> -->
|
||||
</div>
|
||||
<label class="checkboxWithSub typographyCheck">
|
||||
<input type="checkbox" checked />
|
||||
<div>Remove fancy typography</div>
|
||||
<div class="group" data-id="limit">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-step-forward"></i>
|
||||
Limit
|
||||
</div>
|
||||
<div class="sub">
|
||||
Control how many words to generate or for how long you want to type.
|
||||
</div>
|
||||
<div class="groupInputs limitInputs">
|
||||
<input
|
||||
class="words"
|
||||
type="number"
|
||||
value=""
|
||||
min="0"
|
||||
placeholder="words"
|
||||
/>
|
||||
<input
|
||||
class="sections hidden"
|
||||
type="number"
|
||||
value=""
|
||||
min="0"
|
||||
placeholder="sections"
|
||||
/>
|
||||
<div class="or">or</div>
|
||||
<input
|
||||
class="time"
|
||||
type="number"
|
||||
value=""
|
||||
min="0"
|
||||
placeholder="time"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group" data-id="fancy">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-pen-fancy"></i>
|
||||
Remove fancy typography
|
||||
</div>
|
||||
<div class="sub">
|
||||
Standardises typography symbols (for example “ and ” become ")
|
||||
</div>
|
||||
</label>
|
||||
<label class="checkboxWithSub replaceControlCharacters">
|
||||
<input type="checkbox" checked />
|
||||
<div>Replace control characters</div>
|
||||
<div class="groupInputs">
|
||||
<div class="buttonGroup">
|
||||
<button value="false">no</button>
|
||||
<button value="true">yes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group" data-id="control">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-code"></i>
|
||||
Replace control characters
|
||||
</div>
|
||||
<div class="sub">
|
||||
Replace control characters (\n becomes a new line and \t becomes a
|
||||
tab)
|
||||
</div>
|
||||
</label>
|
||||
<label class="checkboxWithSub delimiterCheck">
|
||||
<input type="checkbox" />
|
||||
<div>Pipe delimiter</div>
|
||||
<div class="groupInputs">
|
||||
<div class="buttonGroup">
|
||||
<button value="false">no</button>
|
||||
<button value="true">yes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group" data-id="delimiter">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-grip-lines-vertical"></i>
|
||||
Word delimiter
|
||||
</div>
|
||||
<div class="sub">
|
||||
Change how words are separated. Using the pipe delimiter allows you to
|
||||
randomize groups of words.
|
||||
</div>
|
||||
</label>
|
||||
<label class="checkboxWithSub replaceNewlineWithSpace">
|
||||
<input type="checkbox" />
|
||||
<div>Replace new lines with spaces</div>
|
||||
<div class="groupInputs">
|
||||
<div class="buttonGroup">
|
||||
<button value="true">pipe</button>
|
||||
<button value="false">space</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group" data-id="newlines">
|
||||
<div class="title">
|
||||
<i class="fas fa-fw fa-level-down-alt fa-rotate-90"></i>
|
||||
Replace new lines with spaces
|
||||
</div>
|
||||
<div class="sub">
|
||||
Replace all new line characters with spaces. Can automatically add
|
||||
periods to the end of lines if you wish.
|
||||
</div>
|
||||
</label>
|
||||
<div class="replaceNewLinesButtons disabled">
|
||||
<div class="buttonGroup">
|
||||
<div class="button active noPeriods" data-replace-new-lines="space">
|
||||
no periods
|
||||
</div>
|
||||
<div class="button periods" data-replace-new-lines="period">
|
||||
periods
|
||||
<div class="groupInputs">
|
||||
<div class="buttonGroup">
|
||||
<button value="off">off</button>
|
||||
<button value="space">space</button>
|
||||
<button value="periodSpace">period + space</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -53,11 +53,16 @@
|
|||
#customTextModal {
|
||||
.modal {
|
||||
max-width: 1200px;
|
||||
// grid-template-areas:
|
||||
// "topButtons topButtons topButtons"
|
||||
// "textArea textArea checkboxes"
|
||||
// "ok ok ok";
|
||||
grid-template-areas:
|
||||
"topButtons topButtons topButtons"
|
||||
"topButtons topButtons checkboxes"
|
||||
"textArea textArea checkboxes"
|
||||
"ok ok ok";
|
||||
"ok ok checkboxes";
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: min-content 1fr min-content;
|
||||
|
||||
.buttonsTop {
|
||||
grid-area: topButtons;
|
||||
|
|
@ -75,32 +80,23 @@
|
|||
grid-area: ok;
|
||||
}
|
||||
|
||||
.replaceNewLinesButtons {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
font-size: 0.75rem;
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 1rem;
|
||||
.buttonGroup {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
.button:first-child {
|
||||
border-radius: var(--roundness) 0 0 var(--roundness);
|
||||
}
|
||||
.button:last-child {
|
||||
border-radius: 0 var(--roundness) var(--roundness) 0;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
// .replaceNewLinesButtons {
|
||||
// display: grid;
|
||||
// justify-content: center;
|
||||
// width: 100%;
|
||||
// font-size: 0.75rem;
|
||||
// grid-template-columns: 1fr;
|
||||
// padding: 0 1rem;
|
||||
// &.disabled {
|
||||
// opacity: 0.5;
|
||||
// pointer-events: none;
|
||||
// -webkit-user-select: none;
|
||||
// user-select: none;
|
||||
// }
|
||||
// }
|
||||
|
||||
.longCustomTextWarning {
|
||||
.longCustomTextWarning,
|
||||
.challengeWarning {
|
||||
// background: rgba(0, 0, 0, 0.5);
|
||||
background: var(--sub-alt-color);
|
||||
position: absolute;
|
||||
|
|
@ -126,6 +122,7 @@
|
|||
|
||||
.buttonsTop {
|
||||
display: grid;
|
||||
// grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
|
@ -144,6 +141,7 @@
|
|||
}
|
||||
|
||||
textarea {
|
||||
align-self: start;
|
||||
background: var(--sub-alt-color);
|
||||
padding: 1rem;
|
||||
color: var(--main-color);
|
||||
|
|
@ -154,7 +152,7 @@
|
|||
width: 100%;
|
||||
border-radius: var(--roundness);
|
||||
resize: vertical;
|
||||
height: 450px;
|
||||
min-height: 477px;
|
||||
color: var(--text-color);
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
|
@ -167,28 +165,71 @@
|
|||
align-items: center;
|
||||
justify-items: left;
|
||||
height: min-content;
|
||||
margin: 1rem 0;
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.randomInputFields {
|
||||
// margin: 1rem 0;
|
||||
font-size: 0.75rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
|
||||
.group {
|
||||
display: grid;
|
||||
// gap: 0.5rem;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.title {
|
||||
color: var(--sub-color);
|
||||
text-transform: lowercase;
|
||||
}
|
||||
.sub {
|
||||
// font-size: 0.75em;
|
||||
// height: 0;
|
||||
// overflow: hidden;
|
||||
color: var(--text-color);
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.groupInputs {
|
||||
&.limitInputs {
|
||||
grid-column: 2/-1;
|
||||
display: flex;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
.or {
|
||||
color: var(--sub-color);
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
.buttonGroup {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 0.5rem;
|
||||
button {
|
||||
flex-grow: 1;
|
||||
}
|
||||
// button {
|
||||
// flex-grow: 1;
|
||||
// border-radius: 0;
|
||||
// }
|
||||
// button:first-child {
|
||||
// border-radius: var(--roundness) 0 0 var(--roundness);
|
||||
// }
|
||||
// button:last-child {
|
||||
// border-radius: 0 var(--roundness) var(--roundness) 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@
|
|||
#app {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
#customTextModal {
|
||||
padding: 2rem;
|
||||
.modal {
|
||||
grid-template-columns: 1fr 1fr 2fr;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
// #customTextModal {
|
||||
// padding: 2rem;
|
||||
// .modal {
|
||||
// grid-template-columns: 1fr 1fr 2fr;
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
//1250px
|
||||
|
|
@ -57,8 +57,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
#customTextModal .modal .buttonsTop {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
#customTextModal .modal {
|
||||
textarea {
|
||||
min-height: 426px;
|
||||
}
|
||||
.buttonsTop {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ function canBailOut(): boolean {
|
|||
return (
|
||||
(Config.mode === "custom" && CustomTextState.isCustomTextLong() === true) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isWordRandom &&
|
||||
(CustomText.word >= 5000 || CustomText.word === 0)) ||
|
||||
(CustomText.getLimitMode() === "word" ||
|
||||
CustomText.getLimitMode() === "section") &&
|
||||
(CustomText.getLimit().value >= 5000 ||
|
||||
CustomText.getLimit().value === 0)) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
CustomText.text.length >= 5000) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isTimeRandom &&
|
||||
(CustomText.time >= 3600 || CustomText.time === 0)) ||
|
||||
CustomText.getLimitMode() === "time" &&
|
||||
(CustomText.getLimitValue() >= 3600 ||
|
||||
CustomText.getLimitValue() === 0)) ||
|
||||
(Config.mode === "words" && Config.words >= 5000) ||
|
||||
Config.words === 0 ||
|
||||
(Config.mode === "time" && (Config.time >= 3600 || Config.time === 0)) ||
|
||||
|
|
|
|||
|
|
@ -248,13 +248,13 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
UpdateConfig.setMode("words", true);
|
||||
UpdateConfig.setDifficulty("normal", true);
|
||||
} else if (challenge.type === "customText") {
|
||||
CustomText.setDelimiter(" ");
|
||||
CustomText.setText((challenge.parameters[0] as string).split(" "));
|
||||
CustomText.setIsTimeRandom(false);
|
||||
CustomText.setIsSectionRandom(false);
|
||||
CustomText.setIsWordRandom(challenge.parameters[1] as boolean);
|
||||
CustomText.setWord(challenge.parameters[2] as number);
|
||||
CustomText.setTime(-1);
|
||||
CustomText.setMode(challenge.parameters[1] as SharedTypes.CustomTextMode);
|
||||
CustomText.setLimitValue(challenge.parameters[2] as number);
|
||||
CustomText.setLimitMode(
|
||||
challenge.parameters[3] as SharedTypes.CustomTextLimitMode
|
||||
);
|
||||
CustomText.setPipeDelimiter(challenge.parameters[4] as boolean);
|
||||
UpdateConfig.setMode("custom", true);
|
||||
UpdateConfig.setDifficulty("normal", true);
|
||||
} else if (challenge.type === "script") {
|
||||
|
|
@ -268,13 +268,10 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
let text = scriptdata.trim();
|
||||
text = text.replace(/[\n\r\t ]/gm, " ");
|
||||
text = text.replace(/ +/gm, " ");
|
||||
CustomText.setDelimiter(" ");
|
||||
CustomText.setText(text.split(" "));
|
||||
CustomText.setIsWordRandom(false);
|
||||
CustomText.setIsSectionRandom(false);
|
||||
CustomText.setIsTimeRandom(false);
|
||||
CustomText.setTime(-1);
|
||||
CustomText.setWord(-1);
|
||||
CustomText.setMode("repeat");
|
||||
CustomText.setLimitMode("word");
|
||||
CustomText.setPipeDelimiter(false);
|
||||
UpdateConfig.setMode("custom", true);
|
||||
UpdateConfig.setDifficulty("normal", true);
|
||||
if (challenge.parameters[1] !== null) {
|
||||
|
|
|
|||
|
|
@ -312,8 +312,8 @@ function handleSpace(): void {
|
|||
if (
|
||||
!Config.showAllLines ||
|
||||
Config.mode === "time" ||
|
||||
(CustomText.isWordRandom && CustomText.word === 0) ||
|
||||
CustomText.isTimeRandom
|
||||
(Config.mode === "custom" && CustomText.getLimitValue() === 0) ||
|
||||
(Config.mode === "custom" && CustomText.getLimitMode() === "time")
|
||||
) {
|
||||
const currentTop: number = Math.floor(
|
||||
document.querySelectorAll<HTMLElement>("#words .word")[
|
||||
|
|
@ -1042,7 +1042,7 @@ $(document).on("keydown", async (event) => {
|
|||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
CustomText,
|
||||
CustomText.getData(),
|
||||
CustomTextState.isCustomTextLong() ?? false
|
||||
)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -15,82 +15,119 @@ const popup = "#customTextModal .modal";
|
|||
|
||||
type State = {
|
||||
textarea: string;
|
||||
lastSavedTextareaState: string;
|
||||
longCustomTextWarning: boolean;
|
||||
randomWordsChecked: boolean;
|
||||
randomWordInputs: {
|
||||
challengeWarning: boolean;
|
||||
|
||||
customTextMode: "simple" | SharedTypes.CustomTextMode;
|
||||
customTextLimits: {
|
||||
word: string;
|
||||
time: string;
|
||||
section: string;
|
||||
};
|
||||
pipeDelimiterChecked: boolean;
|
||||
replaceNewlines: "off" | "space" | "period";
|
||||
replaceControlCharactersChecked: boolean;
|
||||
replaceFancyTypographyChecked: boolean;
|
||||
removeFancyTypographyEnabled: boolean;
|
||||
replaceControlCharactersEnabled: boolean;
|
||||
customTextPipeDelimiter: boolean;
|
||||
replaceNewlines: "off" | "space" | "periodSpace";
|
||||
};
|
||||
|
||||
const state: State = {
|
||||
textarea: CustomText.text.join(" "),
|
||||
lastSavedTextareaState: CustomText.text.join(" "),
|
||||
textarea: CustomText.getText().join(
|
||||
CustomText.getPipeDelimiter() ? "|" : " "
|
||||
),
|
||||
longCustomTextWarning: false,
|
||||
randomWordsChecked: false,
|
||||
randomWordInputs: {
|
||||
challengeWarning: false,
|
||||
customTextMode: "simple",
|
||||
customTextLimits: {
|
||||
word: "",
|
||||
time: "",
|
||||
section: "",
|
||||
},
|
||||
pipeDelimiterChecked: false,
|
||||
removeFancyTypographyEnabled: true,
|
||||
replaceControlCharactersEnabled: true,
|
||||
customTextPipeDelimiter: false,
|
||||
replaceNewlines: "off",
|
||||
replaceControlCharactersChecked: true,
|
||||
replaceFancyTypographyChecked: true,
|
||||
};
|
||||
|
||||
function updateUI(): void {
|
||||
if (state.randomWordsChecked) {
|
||||
$(`${popup} .randomWordsCheckbox input`).prop("checked", true);
|
||||
$(`${popup} .inputs .randomInputFields`).removeClass("disabled");
|
||||
} else {
|
||||
$(`${popup} .randomWordsCheckbox input`).prop("checked", false);
|
||||
$(`${popup} .inputs .randomInputFields`).addClass("disabled");
|
||||
$(`${popup} .inputs .group[data-id="mode"] button`).removeClass("active");
|
||||
$(
|
||||
`${popup} .inputs .group[data-id="mode"] button[value="${state.customTextMode}"]`
|
||||
).addClass("active");
|
||||
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.words`).addClass("hidden");
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.sections`).addClass(
|
||||
"hidden"
|
||||
);
|
||||
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.words`).val(
|
||||
state.customTextLimits.word
|
||||
);
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.time`).val(
|
||||
state.customTextLimits.time
|
||||
);
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.sections`).val(
|
||||
state.customTextLimits.section
|
||||
);
|
||||
if (state.customTextLimits.word !== "") {
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.words`).removeClass(
|
||||
"hidden"
|
||||
);
|
||||
}
|
||||
if (state.customTextLimits.section !== "") {
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.sections`).removeClass(
|
||||
"hidden"
|
||||
);
|
||||
}
|
||||
|
||||
if (state.pipeDelimiterChecked) {
|
||||
$(`${popup} .delimiterCheck input`).prop("checked", true);
|
||||
if (state.customTextPipeDelimiter) {
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.sections`).removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.words`).addClass(
|
||||
"hidden"
|
||||
);
|
||||
} else {
|
||||
$(`${popup} .delimiterCheck input`).prop("checked", false);
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.words`).removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(`${popup} .inputs .group[data-id="limit"] input.sections`).addClass(
|
||||
"hidden"
|
||||
);
|
||||
}
|
||||
|
||||
$(`${popup} .replaceNewLinesButtons .button`).removeClass("active");
|
||||
|
||||
if (state.replaceNewlines !== "off") {
|
||||
$(`${popup} .inputs .replaceNewLinesButtons`).removeClass("disabled");
|
||||
$(
|
||||
`${popup} .replaceNewLinesButtons .button[data-replace-new-lines=${state.replaceNewlines}]`
|
||||
).addClass("active");
|
||||
if (state.customTextMode === "simple") {
|
||||
$(`${popup} .inputs .group[data-id="limit"]`).addClass("disabled");
|
||||
$(`${popup} .inputs .group[data-id="limit"] input`).val("");
|
||||
$(`${popup} .inputs .group[data-id="limit"] input`).prop("disabled", true);
|
||||
} else {
|
||||
$(`${popup} .inputs .replaceNewLinesButtons`).addClass("disabled");
|
||||
$(`${popup} .inputs .group[data-id="limit"]`).removeClass("disabled");
|
||||
$(`${popup} .inputs .group[data-id="limit"] input`).prop("disabled", false);
|
||||
}
|
||||
|
||||
$(`${popup} .inputs .group[data-id="fancy"] button`).removeClass("active");
|
||||
$(
|
||||
`${popup} .inputs .group[data-id="fancy"] button[value="${state.removeFancyTypographyEnabled}"]`
|
||||
).addClass("active");
|
||||
|
||||
$(`${popup} .inputs .group[data-id="control"] button`).removeClass("active");
|
||||
$(
|
||||
`${popup} .inputs .group[data-id="control"] button[value="${state.replaceControlCharactersEnabled}"]`
|
||||
).addClass("active");
|
||||
|
||||
$(`${popup} .inputs .group[data-id="delimiter"] button`).removeClass(
|
||||
"active"
|
||||
);
|
||||
$(
|
||||
`${popup} .inputs .group[data-id="delimiter"] button[value="${state.customTextPipeDelimiter}"]`
|
||||
).addClass("active");
|
||||
|
||||
$(`${popup} .inputs .group[data-id="newlines"] button`).removeClass("active");
|
||||
$(
|
||||
`${popup} .inputs .group[data-id="newlines"] button[value="${state.replaceNewlines}"]`
|
||||
).addClass("active");
|
||||
|
||||
$(`${popup} textarea`).val(state.textarea);
|
||||
|
||||
if (state.pipeDelimiterChecked) {
|
||||
$(`${popup} .randomInputFields .sectioncount `).removeClass("hidden");
|
||||
state.randomWordInputs.word = "";
|
||||
$(`${popup} .randomInputFields .wordcount `).addClass("hidden");
|
||||
} else {
|
||||
state.randomWordInputs.section = "";
|
||||
$(`${popup} .randomInputFields .sectioncount `).addClass("hidden");
|
||||
$(`${popup} .randomInputFields .wordcount `).removeClass("hidden");
|
||||
}
|
||||
|
||||
$(`${popup} .randomInputFields .wordcount input`).val(
|
||||
state.randomWordInputs.word
|
||||
);
|
||||
$(`${popup} .randomInputFields .time input`).val(state.randomWordInputs.time);
|
||||
$(`${popup} .randomInputFields .sectioncount input`).val(
|
||||
state.randomWordInputs.section
|
||||
);
|
||||
|
||||
if (state.longCustomTextWarning) {
|
||||
$(`${popup} .longCustomTextWarning`).removeClass("hidden");
|
||||
$(`${popup} .randomWordsCheckbox input`).prop("checked", false);
|
||||
|
|
@ -102,25 +139,48 @@ function updateUI(): void {
|
|||
$(`${popup} .longCustomTextWarning`).addClass("hidden");
|
||||
$(`${popup} .inputs`).removeClass("disabled");
|
||||
}
|
||||
|
||||
if (state.challengeWarning) {
|
||||
$(`${popup} .challengeWarning`).removeClass("hidden");
|
||||
$(`${popup} .randomWordsCheckbox input`).prop("checked", false);
|
||||
$(`${popup} .delimiterCheck input`).prop("checked", false);
|
||||
$(`${popup} .typographyCheck`).prop("checked", true);
|
||||
$(`${popup} .replaceNewlineWithSpace input`).prop("checked", false);
|
||||
$(`${popup} .inputs`).addClass("disabled");
|
||||
} else {
|
||||
$(`${popup} .challengeWarning`).addClass("hidden");
|
||||
$(`${popup} .inputs`).removeClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
async function beforeAnimation(
|
||||
modalEl: HTMLElement,
|
||||
modalChainData?: IncomingData
|
||||
): Promise<void> {
|
||||
state.longCustomTextWarning = CustomTextState.isCustomTextLong() ?? false;
|
||||
state.randomWordsChecked =
|
||||
CustomText.isSectionRandom ||
|
||||
CustomText.isTimeRandom ||
|
||||
CustomText.isWordRandom;
|
||||
state.pipeDelimiterChecked = CustomText.delimiter === "|";
|
||||
// state.replaceNewlinesChecked = false;
|
||||
state.customTextMode = CustomText.getMode();
|
||||
|
||||
if (CustomTextState.isCustomTextLong()) {
|
||||
// if we are in long custom text mode, always reset the textarea state to the current text
|
||||
state.textarea = CustomText.text.join(" ");
|
||||
if (
|
||||
state.customTextMode === "repeat" &&
|
||||
CustomText.getLimitMode() === "word" &&
|
||||
CustomText.getLimitValue() === CustomText.getText().length
|
||||
) {
|
||||
state.customTextMode = "simple";
|
||||
}
|
||||
|
||||
state.customTextLimits.word = "";
|
||||
state.customTextLimits.time = "";
|
||||
state.customTextLimits.section = "";
|
||||
if (CustomText.getLimitMode() === "word") {
|
||||
state.customTextLimits.word = `${CustomText.getLimitValue()}`;
|
||||
} else if (CustomText.getLimitMode() === "time") {
|
||||
state.customTextLimits.time = `${CustomText.getLimitValue()}`;
|
||||
} else if (CustomText.getLimitMode() === "section") {
|
||||
state.customTextLimits.section = `${CustomText.getLimitValue()}`;
|
||||
}
|
||||
state.customTextPipeDelimiter = CustomText.getPipeDelimiter();
|
||||
|
||||
state.longCustomTextWarning = CustomTextState.isCustomTextLong() ?? false;
|
||||
|
||||
if (modalChainData?.text !== undefined) {
|
||||
if (modalChainData.long !== true && CustomTextState.isCustomTextLong()) {
|
||||
CustomTextState.setCustomTextName("", undefined);
|
||||
|
|
@ -135,26 +195,25 @@ async function beforeAnimation(
|
|||
? modalChainData.text
|
||||
: state.textarea + " " + modalChainData.text;
|
||||
state.textarea = newText;
|
||||
state.customTextMode = "simple";
|
||||
state.customTextLimits.word = `${cleanUpText().length}`;
|
||||
state.customTextLimits.time = "";
|
||||
state.customTextLimits.section = "";
|
||||
}
|
||||
|
||||
state.randomWordInputs.word =
|
||||
CustomText.word === -1 ? "" : `${CustomText.word}`;
|
||||
state.randomWordInputs.time =
|
||||
CustomText.time === -1 ? "" : `${CustomText.time}`;
|
||||
state.randomWordInputs.section =
|
||||
CustomText.section === -1 ? "" : `${CustomText.section}`;
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
async function afterAnimation(): Promise<void> {
|
||||
if (!CustomTextState.isCustomTextLong()) {
|
||||
if (!state.challengeWarning && !state.longCustomTextWarning) {
|
||||
$(`${popup} textarea`).trigger("focus");
|
||||
}
|
||||
}
|
||||
|
||||
export function show(showOptions?: ShowOptions): void {
|
||||
state.textarea = state.lastSavedTextareaState;
|
||||
state.textarea = CustomText.getText().join(
|
||||
CustomText.getPipeDelimiter() ? "|" : " "
|
||||
);
|
||||
void modal.show({
|
||||
...(showOptions as ShowOptions<IncomingData>),
|
||||
beforeAnimation,
|
||||
|
|
@ -207,7 +266,7 @@ function cleanUpText(): string[] {
|
|||
//replace zero width characters
|
||||
text = text.replace(/[\u200B-\u200D\u2060\uFEFF]/g, "");
|
||||
|
||||
if (state.replaceControlCharactersChecked) {
|
||||
if (state.replaceControlCharactersEnabled) {
|
||||
text = text.replace(/([^\\]|^)\\t/gm, "$1\t");
|
||||
text = text.replace(/([^\\]|^)\\n/gm, "$1\n");
|
||||
text = text.replace(/\\\\t/gm, "\\t");
|
||||
|
|
@ -216,12 +275,12 @@ function cleanUpText(): string[] {
|
|||
|
||||
text = text.replace(/ +/gm, " ");
|
||||
text = text.replace(/( *(\r\n|\r|\n) *)/g, "\n ");
|
||||
if (state.replaceFancyTypographyChecked) {
|
||||
if (state.removeFancyTypographyEnabled) {
|
||||
text = Misc.cleanTypographySymbols(text);
|
||||
}
|
||||
|
||||
if (state.replaceNewlines !== "off") {
|
||||
const periods = state.replaceNewlines === "period";
|
||||
const periods = state.replaceNewlines === "periodSpace";
|
||||
if (periods) {
|
||||
text = text.replace(/\n/gm, ". ");
|
||||
text = text.replace(/\.\. /gm, ". ");
|
||||
|
|
@ -232,7 +291,9 @@ function cleanUpText(): string[] {
|
|||
}
|
||||
}
|
||||
|
||||
const words = text.split(CustomText.delimiter).filter((word) => word !== "");
|
||||
const words = text
|
||||
.split(state.customTextPipeDelimiter ? "|" : " ")
|
||||
.filter((word) => word !== "");
|
||||
return words;
|
||||
}
|
||||
|
||||
|
|
@ -242,57 +303,38 @@ function apply(): void {
|
|||
return;
|
||||
}
|
||||
|
||||
state.lastSavedTextareaState = state.textarea;
|
||||
|
||||
CustomText.setText(cleanUpText());
|
||||
|
||||
CustomText.setWord(parseInt(state.randomWordInputs.word || "-1"));
|
||||
CustomText.setTime(parseInt(state.randomWordInputs.time || "-1"));
|
||||
CustomText.setSection(parseInt(state.randomWordInputs.section || "-1"));
|
||||
|
||||
CustomText.setIsWordRandom(state.randomWordsChecked && CustomText.word > -1);
|
||||
CustomText.setIsTimeRandom(state.randomWordsChecked && CustomText.time > -1);
|
||||
CustomText.setIsSectionRandom(
|
||||
state.randomWordsChecked && CustomText.section > -1
|
||||
);
|
||||
if (
|
||||
state.randomWordsChecked &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isSectionRandom
|
||||
[
|
||||
state.customTextLimits.word,
|
||||
state.customTextLimits.time,
|
||||
state.customTextLimits.section,
|
||||
].filter((limit) => limit !== "").length > 1
|
||||
) {
|
||||
Notifications.add(
|
||||
"You need to specify word count or time in seconds to start a random custom test",
|
||||
0,
|
||||
{
|
||||
duration: 5,
|
||||
}
|
||||
);
|
||||
Notifications.add("You can only specify one limit", 0, {
|
||||
duration: 5,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// ($(`${popup} .randomWordsCheckbox input`).prop("checked") as boolean) &&
|
||||
state.randomWordsChecked &&
|
||||
CustomText.isTimeRandom &&
|
||||
CustomText.isWordRandom
|
||||
state.customTextMode !== "simple" &&
|
||||
state.customTextLimits.word === "" &&
|
||||
state.customTextLimits.time === "" &&
|
||||
state.customTextLimits.section === ""
|
||||
) {
|
||||
Notifications.add(
|
||||
"You need to pick between word count or time in seconds to start a random custom test",
|
||||
0,
|
||||
{
|
||||
duration: 5,
|
||||
}
|
||||
);
|
||||
Notifications.add("You need to specify a limit", 0, {
|
||||
duration: 5,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(CustomText.isWordRandom && CustomText.word === 0) ||
|
||||
(CustomText.isTimeRandom && CustomText.time === 0)
|
||||
state.customTextLimits.section === "0" ||
|
||||
state.customTextLimits.word === "0" ||
|
||||
state.customTextLimits.time === "0"
|
||||
) {
|
||||
Notifications.add(
|
||||
"Infinite words! Make sure to use Bail Out from the command line to save your result.",
|
||||
"Infinite test! Make sure to use Bail Out from the command line to save your result.",
|
||||
0,
|
||||
{
|
||||
duration: 7,
|
||||
|
|
@ -300,6 +342,28 @@ function apply(): void {
|
|||
);
|
||||
}
|
||||
|
||||
const text = cleanUpText();
|
||||
|
||||
if (state.customTextMode === "simple") {
|
||||
CustomText.setMode("repeat");
|
||||
} else {
|
||||
CustomText.setMode(state.customTextMode);
|
||||
}
|
||||
|
||||
CustomText.setPipeDelimiter(state.customTextPipeDelimiter);
|
||||
CustomText.setText(text);
|
||||
|
||||
if (state.customTextLimits.word !== "") {
|
||||
CustomText.setLimitMode("word");
|
||||
CustomText.setLimitValue(parseInt(state.customTextLimits.word));
|
||||
} else if (state.customTextLimits.time !== "") {
|
||||
CustomText.setLimitMode("time");
|
||||
CustomText.setLimitValue(parseInt(state.customTextLimits.time));
|
||||
} else if (state.customTextLimits.section !== "") {
|
||||
CustomText.setLimitMode("section");
|
||||
CustomText.setLimitValue(parseInt(state.customTextLimits.section));
|
||||
}
|
||||
|
||||
ChallengeController.clearActive();
|
||||
ManualRestart.set();
|
||||
if (Config.mode !== "custom") UpdateConfig.setMode("custom");
|
||||
|
|
@ -307,67 +371,115 @@ function apply(): void {
|
|||
hide();
|
||||
}
|
||||
|
||||
function handleDelimiterChange(): void {
|
||||
let newtext = state.textarea
|
||||
.split(state.customTextPipeDelimiter ? " " : "|")
|
||||
.join(state.customTextPipeDelimiter ? "|" : " ");
|
||||
newtext = newtext.replace(/\n /g, "\n");
|
||||
state.textarea = newtext;
|
||||
}
|
||||
|
||||
async function setup(modalEl: HTMLElement): Promise<void> {
|
||||
modalEl
|
||||
.querySelector("#fileInput")
|
||||
?.addEventListener("change", handleFileOpen);
|
||||
modalEl
|
||||
.querySelector(".randomWordsCheckbox input")
|
||||
?.addEventListener("change", (e) => {
|
||||
state.randomWordsChecked = (e.target as HTMLInputElement).checked;
|
||||
updateUI();
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".typographyCheck input")
|
||||
?.addEventListener("change", (e) => {
|
||||
state.replaceFancyTypographyChecked = (
|
||||
e.target as HTMLInputElement
|
||||
).checked;
|
||||
updateUI();
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".delimiterCheck input")
|
||||
?.addEventListener("change", (e) => {
|
||||
state.pipeDelimiterChecked = (e.target as HTMLInputElement).checked;
|
||||
if (state.textarea !== CustomText.text.join(CustomText.delimiter)) {
|
||||
const currentTextSplit = state.textarea.split(CustomText.delimiter);
|
||||
let newtext = currentTextSplit.join(
|
||||
state.pipeDelimiterChecked ? "|" : " "
|
||||
);
|
||||
newtext = newtext.replace(/\n /g, "\n");
|
||||
state.textarea = newtext;
|
||||
} else {
|
||||
let newtext = CustomText.text.join(
|
||||
state.pipeDelimiterChecked ? "|" : " "
|
||||
);
|
||||
newtext = newtext.replace(/\n /g, "\n");
|
||||
state.textarea = newtext;
|
||||
}
|
||||
CustomText.setDelimiter(state.pipeDelimiterChecked ? "|" : " ");
|
||||
updateUI();
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".replaceNewlineWithSpace input")
|
||||
?.addEventListener("change", (e) => {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
if (checked === false) {
|
||||
state.replaceNewlines = "off";
|
||||
} else {
|
||||
state.replaceNewlines = "space";
|
||||
}
|
||||
updateUI();
|
||||
});
|
||||
const replaceNewLinesButtons = modalEl.querySelectorAll(
|
||||
".replaceNewLinesButtons .button"
|
||||
);
|
||||
for (const button of replaceNewLinesButtons) {
|
||||
|
||||
const buttons = modalEl.querySelectorAll(".group[data-id='mode'] button");
|
||||
for (const button of buttons) {
|
||||
button.addEventListener("click", (e) => {
|
||||
state.replaceNewlines = (e.target as HTMLElement).dataset[
|
||||
"replaceNewLines"
|
||||
] as "space" | "period";
|
||||
state.customTextMode = (e.target as HTMLButtonElement).value as
|
||||
| "simple"
|
||||
| "repeat"
|
||||
| "random";
|
||||
if (state.customTextMode === "simple") {
|
||||
const text = cleanUpText();
|
||||
state.customTextLimits.word = `${text.length}`;
|
||||
state.customTextLimits.time = "";
|
||||
state.customTextLimits.section = "";
|
||||
}
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
for (const button of modalEl.querySelectorAll(
|
||||
".group[data-id='fancy'] button"
|
||||
)) {
|
||||
button.addEventListener("click", (e) => {
|
||||
state.removeFancyTypographyEnabled =
|
||||
(e.target as HTMLButtonElement).value === "true" ? true : false;
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
for (const button of modalEl.querySelectorAll(
|
||||
".group[data-id='control'] button"
|
||||
)) {
|
||||
button.addEventListener("click", (e) => {
|
||||
state.replaceControlCharactersEnabled =
|
||||
(e.target as HTMLButtonElement).value === "true" ? true : false;
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
for (const button of modalEl.querySelectorAll(
|
||||
".group[data-id='delimiter'] button"
|
||||
)) {
|
||||
button.addEventListener("click", (e) => {
|
||||
state.customTextPipeDelimiter =
|
||||
(e.target as HTMLButtonElement).value === "true" ? true : false;
|
||||
if (state.customTextPipeDelimiter && state.customTextLimits.word !== "") {
|
||||
state.customTextLimits.word = "";
|
||||
}
|
||||
if (
|
||||
!state.customTextPipeDelimiter &&
|
||||
state.customTextLimits.section !== ""
|
||||
) {
|
||||
state.customTextLimits.section = "";
|
||||
}
|
||||
handleDelimiterChange();
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
for (const button of modalEl.querySelectorAll(
|
||||
".group[data-id='newlines'] button"
|
||||
)) {
|
||||
button.addEventListener("click", (e) => {
|
||||
state.replaceNewlines = (e.target as HTMLButtonElement).value as
|
||||
| "off"
|
||||
| "space"
|
||||
| "periodSpace";
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
modalEl
|
||||
.querySelector(".group[data-id='limit'] input.words")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.customTextLimits.word = (e.target as HTMLInputElement).value;
|
||||
state.customTextLimits.time = "";
|
||||
state.customTextLimits.section = "";
|
||||
updateUI();
|
||||
});
|
||||
|
||||
modalEl
|
||||
.querySelector(".group[data-id='limit'] input.time")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.customTextLimits.time = (e.target as HTMLInputElement).value;
|
||||
state.customTextLimits.word = "";
|
||||
state.customTextLimits.section = "";
|
||||
updateUI();
|
||||
});
|
||||
|
||||
modalEl
|
||||
.querySelector(".group[data-id='limit'] input.sections")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.customTextLimits.section = (e.target as HTMLInputElement).value;
|
||||
state.customTextLimits.word = "";
|
||||
state.customTextLimits.time = "";
|
||||
updateUI();
|
||||
});
|
||||
|
||||
const textarea = modalEl.querySelector("textarea");
|
||||
textarea?.addEventListener("input", (e) => {
|
||||
state.textarea = (e.target as HTMLTextAreaElement).value;
|
||||
|
|
@ -390,7 +502,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
|
|||
state.textarea = area.value;
|
||||
});
|
||||
textarea?.addEventListener("keypress", (e) => {
|
||||
if (state.longCustomTextWarning) {
|
||||
if (state.longCustomTextWarning || state.challengeWarning) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
|
@ -408,30 +520,6 @@ async function setup(modalEl: HTMLElement): Promise<void> {
|
|||
});
|
||||
}
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".randomInputFields .wordcount input")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.randomWordInputs.time = "";
|
||||
state.randomWordInputs.word = (e.target as HTMLInputElement).value;
|
||||
state.randomWordInputs.section = "";
|
||||
updateUI();
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".randomInputFields .time input")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.randomWordInputs.time = (e.target as HTMLInputElement).value;
|
||||
state.randomWordInputs.word = "";
|
||||
state.randomWordInputs.section = "";
|
||||
updateUI();
|
||||
});
|
||||
modalEl
|
||||
.querySelector(".randomInputFields .sectioncount input")
|
||||
?.addEventListener("input", (e) => {
|
||||
state.randomWordInputs.time = "";
|
||||
state.randomWordInputs.word = "";
|
||||
state.randomWordInputs.section = (e.target as HTMLInputElement).value;
|
||||
updateUI();
|
||||
});
|
||||
modalEl.querySelector(".button.apply")?.addEventListener("click", () => {
|
||||
apply();
|
||||
});
|
||||
|
|
@ -461,6 +549,10 @@ async function setup(modalEl: HTMLElement): Promise<void> {
|
|||
state.longCustomTextWarning = false;
|
||||
updateUI();
|
||||
});
|
||||
modalEl.querySelector(".challengeWarning")?.addEventListener("click", () => {
|
||||
state.challengeWarning = false;
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
type IncomingData = {
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ export function show(): void {
|
|||
});
|
||||
}
|
||||
|
||||
function hide(): void {
|
||||
void modal.hide();
|
||||
}
|
||||
// function hide(): void {
|
||||
// void modal.hide();
|
||||
// }
|
||||
|
||||
async function setup(modalEl: HTMLElement): Promise<void> {
|
||||
const wordsGroupButtons = modalEl.querySelectorAll(".wordsGroup button");
|
||||
|
|
|
|||
|
|
@ -256,7 +256,9 @@ async function apply(set: boolean): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
const customText = filteredWords.join(CustomText.delimiter);
|
||||
const customText = filteredWords.join(
|
||||
CustomText.getPipeDelimiter() ? "|" : " "
|
||||
);
|
||||
|
||||
hide({
|
||||
modalChainData: {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function getCheckboxValue(checkbox: string): boolean {
|
|||
type SharedTestSettings = [
|
||||
SharedTypes.Config.Mode | null,
|
||||
SharedTypes.Config.Mode2<SharedTypes.Config.Mode> | null,
|
||||
SharedTypes.CustomText | null,
|
||||
SharedTypes.CustomTextData | null,
|
||||
boolean | null,
|
||||
boolean | null,
|
||||
string | null,
|
||||
|
|
@ -49,14 +49,7 @@ function updateURL(): void {
|
|||
}
|
||||
|
||||
if (getCheckboxValue("customText")) {
|
||||
settings[2] = {
|
||||
text: CustomText.text,
|
||||
isWordRandom: CustomText.isWordRandom,
|
||||
isTimeRandom: CustomText.isTimeRandom,
|
||||
word: CustomText.word,
|
||||
time: CustomText.time,
|
||||
delimiter: CustomText.delimiter,
|
||||
};
|
||||
settings[2] = CustomText.getData();
|
||||
}
|
||||
|
||||
if (getCheckboxValue("punctuation")) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import Config from "../config";
|
||||
import { capitalizeFirstLetterOfEachWord } from "../utils/strings";
|
||||
import { cachedFetchJson } from "../utils/json-data";
|
||||
import * as CustomText from "../test/custom-text";
|
||||
|
||||
type BritishEnglishReplacement = {
|
||||
0: string;
|
||||
|
|
@ -39,14 +38,7 @@ export async function replace(
|
|||
|
||||
if (!replacement) return word;
|
||||
|
||||
if (
|
||||
(Config.mode === "quote" ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isSectionRandom)) &&
|
||||
replacement[2]?.includes(previousWord)
|
||||
) {
|
||||
if (Config.mode === "quote" && replacement[2]?.includes(previousWord)) {
|
||||
return word;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export let text = [
|
||||
let text: string[] = [
|
||||
"The",
|
||||
"quick",
|
||||
"brown",
|
||||
|
|
@ -9,52 +9,67 @@ export let text = [
|
|||
"lazy",
|
||||
"dog",
|
||||
];
|
||||
export let isWordRandom = false;
|
||||
export let isTimeRandom = false;
|
||||
export let isSectionRandom = false;
|
||||
export let word = -1;
|
||||
export let time = -1;
|
||||
export let section = -1;
|
||||
export let delimiter = " ";
|
||||
|
||||
export function setText(txt: string[]): void {
|
||||
text = txt;
|
||||
}
|
||||
let mode: SharedTypes.CustomTextMode = "repeat";
|
||||
const limit: SharedTypes.CustomTextLimit = {
|
||||
value: 9,
|
||||
mode: "word",
|
||||
};
|
||||
let pipeDelimiter = false;
|
||||
|
||||
export function getText(): string {
|
||||
return text.join(" ");
|
||||
}
|
||||
|
||||
export function getTextArray(): string[] {
|
||||
export function getText(): string[] {
|
||||
return text;
|
||||
}
|
||||
|
||||
export function setIsWordRandom(val: boolean): void {
|
||||
isWordRandom = val;
|
||||
export function setText(txt: string[]): void {
|
||||
text = txt;
|
||||
limit.value = text.length;
|
||||
}
|
||||
|
||||
export function setIsTimeRandom(val: boolean): void {
|
||||
isTimeRandom = val;
|
||||
export function getMode(): SharedTypes.CustomTextMode {
|
||||
return mode;
|
||||
}
|
||||
|
||||
export function setIsSectionRandom(val: boolean): void {
|
||||
isSectionRandom = val;
|
||||
export function setMode(val: SharedTypes.CustomTextMode): void {
|
||||
mode = val;
|
||||
limit.value = text.length;
|
||||
}
|
||||
|
||||
export function setTime(val: number): void {
|
||||
time = val;
|
||||
export function getLimit(): SharedTypes.CustomTextLimit {
|
||||
return limit;
|
||||
}
|
||||
|
||||
export function setWord(val: number): void {
|
||||
word = val;
|
||||
export function getLimitValue(): number {
|
||||
return limit.value;
|
||||
}
|
||||
|
||||
export function setSection(val: number): void {
|
||||
section = val;
|
||||
export function getLimitMode(): SharedTypes.CustomTextLimitMode {
|
||||
return limit.mode;
|
||||
}
|
||||
|
||||
export function setDelimiter(val: string): void {
|
||||
delimiter = val;
|
||||
export function setLimitValue(val: number): void {
|
||||
limit.value = val;
|
||||
}
|
||||
|
||||
export function setLimitMode(val: SharedTypes.CustomTextLimitMode): void {
|
||||
limit.mode = val;
|
||||
}
|
||||
|
||||
export function getPipeDelimiter(): boolean {
|
||||
return pipeDelimiter;
|
||||
}
|
||||
|
||||
export function setPipeDelimiter(val: boolean): void {
|
||||
pipeDelimiter = val;
|
||||
}
|
||||
|
||||
export function getData(): SharedTypes.CustomTextData {
|
||||
return {
|
||||
text,
|
||||
mode,
|
||||
limit,
|
||||
pipeDelimiter,
|
||||
};
|
||||
}
|
||||
|
||||
type CustomTextObject = Record<string, string>;
|
||||
|
|
|
|||
|
|
@ -10,19 +10,11 @@ import { isPopupVisible } from "../utils/misc";
|
|||
|
||||
const wrapperId = "practiseWordsPopupWrapper";
|
||||
|
||||
type BeforeCustomText = {
|
||||
text: string[];
|
||||
isTimeRandom: boolean;
|
||||
isWordRandom: boolean;
|
||||
time: number;
|
||||
word: number;
|
||||
};
|
||||
|
||||
type Before = {
|
||||
mode: SharedTypes.Config.Mode | null;
|
||||
punctuation: boolean | null;
|
||||
numbers: boolean | null;
|
||||
customText: BeforeCustomText | null;
|
||||
customText: SharedTypes.CustomTextData | null;
|
||||
};
|
||||
|
||||
export const before: Before = {
|
||||
|
|
@ -106,23 +98,15 @@ export function init(missed: boolean, slow: boolean): boolean {
|
|||
|
||||
let customText = null;
|
||||
if (Config.mode === "custom") {
|
||||
customText = {
|
||||
text: CustomText.text,
|
||||
isWordRandom: CustomText.isWordRandom,
|
||||
isTimeRandom: CustomText.isTimeRandom,
|
||||
word: CustomText.word,
|
||||
time: CustomText.time,
|
||||
};
|
||||
customText = CustomText.getData();
|
||||
}
|
||||
|
||||
UpdateConfig.setMode("custom", true);
|
||||
CustomText.setText(newCustomText);
|
||||
CustomText.setIsWordRandom(true);
|
||||
CustomText.setIsTimeRandom(false);
|
||||
CustomText.setWord(
|
||||
CustomText.setLimitMode("word");
|
||||
CustomText.setLimitValue(
|
||||
(sortableSlowWords.length + sortableMissedWords.length) * 5
|
||||
);
|
||||
CustomText.setTime(-1);
|
||||
|
||||
setCustomTextName("practise", undefined);
|
||||
|
||||
|
|
|
|||
|
|
@ -855,7 +855,7 @@ export async function update(
|
|||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
CustomText,
|
||||
CustomText.getData(),
|
||||
CustomTextState.isCustomTextLong() ?? false
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ export function restart(options = {} as RestartOptions): void {
|
|||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
CustomText,
|
||||
CustomText.getData(),
|
||||
CustomTextState.isCustomTextLong() ?? false
|
||||
)
|
||||
) {
|
||||
|
|
@ -225,10 +225,11 @@ export function restart(options = {} as RestartOptions): void {
|
|||
|
||||
if (PractiseWords.before.customText) {
|
||||
CustomText.setText(PractiseWords.before.customText.text);
|
||||
CustomText.setIsTimeRandom(PractiseWords.before.customText.isTimeRandom);
|
||||
CustomText.setIsWordRandom(PractiseWords.before.customText.isWordRandom);
|
||||
CustomText.setWord(PractiseWords.before.customText.word);
|
||||
CustomText.setTime(PractiseWords.before.customText.time);
|
||||
CustomText.setLimitMode(PractiseWords.before.customText.limit.mode);
|
||||
CustomText.setLimitValue(PractiseWords.before.customText.limit.value);
|
||||
CustomText.setPipeDelimiter(
|
||||
PractiseWords.before.customText.pipeDelimiter
|
||||
);
|
||||
}
|
||||
|
||||
UpdateConfig.setMode(PractiseWords.before.mode);
|
||||
|
|
@ -537,22 +538,17 @@ export async function addWord(): Promise<void> {
|
|||
TestWords.words.length >= Config.words &&
|
||||
Config.words > 0) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isWordRandom &&
|
||||
TestWords.words.length >= CustomText.word &&
|
||||
CustomText.word !== 0) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isSectionRandom &&
|
||||
TestWords.words.length >= CustomText.text.length) ||
|
||||
CustomText.getLimitMode() === "word" &&
|
||||
TestWords.words.length >= CustomText.getLimitValue() &&
|
||||
CustomText.getLimitValue() !== 0) ||
|
||||
(Config.mode === "quote" &&
|
||||
TestWords.words.length >=
|
||||
(TestWords.randomQuote?.textSplit?.length ?? 0)) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isSectionRandom &&
|
||||
WordsGenerator.sectionIndex >= CustomText.section &&
|
||||
CustomText.getLimitMode() === "section" &&
|
||||
WordsGenerator.sectionIndex >= CustomText.getLimitValue() &&
|
||||
WordsGenerator.currentSection.length === 0 &&
|
||||
CustomText.section !== 0)
|
||||
CustomText.getLimitValue() !== 0)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -597,7 +593,7 @@ export async function addWord(): Promise<void> {
|
|||
: {
|
||||
//borrow the direction of the current language
|
||||
...(await JSONData.getCurrentLanguage(Config.language)),
|
||||
words: CustomText.text,
|
||||
words: CustomText.getText(),
|
||||
};
|
||||
const wordset = await Wordset.withWords(language.words);
|
||||
|
||||
|
|
@ -748,14 +744,15 @@ function buildCompletedEvent(
|
|||
const wpmCons = Numbers.roundTo2(Misc.kogasa(stddev3 / avg3));
|
||||
const wpmConsistency = isNaN(wpmCons) ? 0 : wpmCons;
|
||||
|
||||
let customText: SharedTypes.CustomText | null = null;
|
||||
let customText: SharedTypes.CustomTextDataWithTextLen | null = null;
|
||||
if (Config.mode === "custom") {
|
||||
customText = {} as SharedTypes.CustomText;
|
||||
customText.textLen = CustomText.text.length;
|
||||
customText.isWordRandom = CustomText.isWordRandom;
|
||||
customText.isTimeRandom = CustomText.isTimeRandom;
|
||||
customText.word = CustomText.word;
|
||||
customText.time = CustomText.time;
|
||||
const temp = CustomText.getData();
|
||||
customText = {
|
||||
textLen: temp.text.length,
|
||||
mode: temp.mode,
|
||||
pipeDelimiter: temp.pipeDelimiter,
|
||||
limit: temp.limit,
|
||||
};
|
||||
}
|
||||
|
||||
//tags
|
||||
|
|
@ -875,6 +872,8 @@ export async function finish(difficultyFailed = false): Promise<void> {
|
|||
|
||||
const ce = buildCompletedEvent(difficultyFailed);
|
||||
|
||||
console.debug("Completed event object", ce);
|
||||
|
||||
function countUndefined(input: unknown): number {
|
||||
if (typeof input === "number") {
|
||||
return isNaN(input) ? 1 : 0;
|
||||
|
|
@ -935,25 +934,12 @@ export async function finish(difficultyFailed = false): Promise<void> {
|
|||
mode2Number === 0 &&
|
||||
completedEvent.testDuration < 15) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isSectionRandom &&
|
||||
CustomText.text.length < 10) ||
|
||||
(CustomText.getLimitMode() === "word" ||
|
||||
CustomText.getLimitMode() === "section") &&
|
||||
CustomText.getLimitValue() < 10) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isSectionRandom &&
|
||||
CustomText.word < 10) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isSectionRandom &&
|
||||
CustomText.isTimeRandom &&
|
||||
CustomText.time < 15) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
CustomText.isSectionRandom &&
|
||||
TestWords.words.length < 10) ||
|
||||
CustomText.getLimitMode() === "time" &&
|
||||
CustomText.getLimitValue() < 15) ||
|
||||
(Config.mode === "zen" && completedEvent.testDuration < 15)
|
||||
) {
|
||||
Notifications.add("Test invalid - too short", 0);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ function updateTimer(): void {
|
|||
if (timerDebug) console.time("timer progress update");
|
||||
if (
|
||||
Config.mode === "time" ||
|
||||
(Config.mode === "custom" && CustomText.isTimeRandom)
|
||||
(Config.mode === "custom" && CustomText.getLimitMode() === "time")
|
||||
) {
|
||||
TimerProgress.update();
|
||||
}
|
||||
|
|
@ -149,14 +149,14 @@ function checkIfTimeIsUp(): void {
|
|||
if (timerDebug) console.time("times up check");
|
||||
if (
|
||||
Config.mode === "time" ||
|
||||
(Config.mode === "custom" && CustomText.isTimeRandom)
|
||||
(Config.mode === "custom" && CustomText.getLimitMode() === "time")
|
||||
) {
|
||||
if (
|
||||
(Time.get() >= Config.time &&
|
||||
Config.time !== 0 &&
|
||||
Config.mode === "time") ||
|
||||
(Time.get() >= CustomText.time &&
|
||||
CustomText.time !== 0 &&
|
||||
(Time.get() >= CustomText.getLimitValue() &&
|
||||
CustomText.getLimitValue() !== 0 &&
|
||||
Config.mode === "custom")
|
||||
) {
|
||||
//times up
|
||||
|
|
|
|||
|
|
@ -435,8 +435,8 @@ function updateWordsHeight(force = false): void {
|
|||
if (
|
||||
Config.showAllLines &&
|
||||
Config.mode !== "time" &&
|
||||
!(CustomText.isWordRandom && CustomText.word === 0) &&
|
||||
!CustomText.isTimeRandom
|
||||
CustomText.getLimitMode() !== "time" &&
|
||||
CustomText.getLimitValue() !== 0
|
||||
) {
|
||||
$("#words")
|
||||
.css("height", "auto")
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ const miniTimerNumberElement = document.querySelector(
|
|||
);
|
||||
|
||||
function getCurrentCount(): number {
|
||||
if (Config.mode === "custom" && CustomText.isSectionRandom) {
|
||||
if (Config.mode === "custom" && CustomText.getLimitMode() === "section") {
|
||||
return (
|
||||
(TestWords.words.sectionIndexList[
|
||||
TestWords.words.currentIndex
|
||||
|
|
@ -110,11 +110,11 @@ export function update(): void {
|
|||
const time = Time.get();
|
||||
if (
|
||||
Config.mode === "time" ||
|
||||
(Config.mode === "custom" && CustomText.isTimeRandom)
|
||||
(Config.mode === "custom" && CustomText.getLimitMode() === "time")
|
||||
) {
|
||||
let maxtime = Config.time;
|
||||
if (Config.mode === "custom" && CustomText.isTimeRandom) {
|
||||
maxtime = CustomText.time;
|
||||
if (Config.mode === "custom" && CustomText.getLimitMode() === "time") {
|
||||
maxtime = CustomText.getLimitValue();
|
||||
}
|
||||
if (Config.timerStyle === "bar") {
|
||||
const percent = 100 - ((time + 1) / maxtime) * 100;
|
||||
|
|
@ -154,13 +154,14 @@ export function update(): void {
|
|||
outof = Config.words;
|
||||
}
|
||||
if (Config.mode === "custom") {
|
||||
if (CustomText.isWordRandom) {
|
||||
outof = CustomText.word;
|
||||
} else if (CustomText.isSectionRandom) {
|
||||
outof = CustomText.section;
|
||||
} else {
|
||||
outof = CustomText.text.length;
|
||||
}
|
||||
outof = CustomText.getLimitValue();
|
||||
// if (CustomText.getLimitMode() === "word") {
|
||||
// outof = CustomText.word;
|
||||
// } else if (CustomText.isSectionRandom) {
|
||||
// outof = CustomText.section;
|
||||
// } else {
|
||||
// outof = CustomText.text.length;
|
||||
// }
|
||||
}
|
||||
if (Config.mode === "quote") {
|
||||
outof = TestWords.randomQuote?.textSplit?.length ?? 1;
|
||||
|
|
|
|||
|
|
@ -392,11 +392,7 @@ export function getWordsLimit(): number {
|
|||
|
||||
if (Config.showAllLines) {
|
||||
if (Config.mode === "custom") {
|
||||
if (CustomText.isWordRandom) {
|
||||
limit = CustomText.word;
|
||||
} else if (!CustomText.isTimeRandom && !CustomText.isWordRandom) {
|
||||
limit = CustomText.text.length;
|
||||
}
|
||||
limit = CustomText.getLimitValue();
|
||||
}
|
||||
if (Config.mode === "words") {
|
||||
limit = Config.words;
|
||||
|
|
@ -407,15 +403,17 @@ export function getWordsLimit(): number {
|
|||
if (Config.mode === "words" && Config.words === 0) {
|
||||
limit = 100;
|
||||
}
|
||||
if (
|
||||
Config.mode === "custom" &&
|
||||
CustomText.isWordRandom &&
|
||||
CustomText.word === 0
|
||||
) {
|
||||
limit = 100;
|
||||
}
|
||||
if (Config.mode === "custom" && CustomText.delimiter === "|") {
|
||||
limit = 100;
|
||||
if (Config.mode === "custom") {
|
||||
if (
|
||||
CustomText.getLimitValue() === 0 ||
|
||||
CustomText.getLimitMode() === "time" ||
|
||||
CustomText.getLimitMode() === "section"
|
||||
) {
|
||||
limit = 100;
|
||||
} else {
|
||||
limit =
|
||||
CustomText.getLimitValue() > 100 ? 100 : CustomText.getLimitValue();
|
||||
}
|
||||
}
|
||||
|
||||
//funboxes
|
||||
|
|
@ -427,35 +425,6 @@ export function getWordsLimit(): number {
|
|||
if (Config.mode === "words" && Config.words !== 0 && Config.words < limit) {
|
||||
limit = Config.words;
|
||||
}
|
||||
if (
|
||||
Config.mode === "custom" &&
|
||||
!CustomText.isSectionRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
CustomText.isWordRandom &&
|
||||
CustomText.word !== 0 &&
|
||||
CustomText.word < limit
|
||||
) {
|
||||
limit = CustomText.word;
|
||||
}
|
||||
if (
|
||||
Config.mode === "custom" &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isSectionRandom &&
|
||||
CustomText.text.length !== 0 &&
|
||||
CustomText.text.length < limit
|
||||
) {
|
||||
let newLimit = 0;
|
||||
for (const word of CustomText.text) {
|
||||
if (/ /g.test(word)) {
|
||||
newLimit += word.split(" ").length;
|
||||
} else {
|
||||
newLimit++;
|
||||
}
|
||||
}
|
||||
|
||||
limit = newLimit;
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
|
@ -499,6 +468,7 @@ export async function generateWords(
|
|||
sectionFunbox?.functions?.pullSection !== undefined;
|
||||
|
||||
const limit = getWordsLimit();
|
||||
console.debug("Words limit", limit);
|
||||
|
||||
const wordOrder = getQuoteOrCustomModeWordOrder();
|
||||
console.debug("Word order", wordOrder);
|
||||
|
|
@ -506,9 +476,9 @@ export async function generateWords(
|
|||
let wordList = language.words;
|
||||
if (Config.mode === "custom") {
|
||||
if (wordOrder === "reverse") {
|
||||
wordList = CustomText.text.reverse();
|
||||
wordList = CustomText.getText().reverse();
|
||||
} else {
|
||||
wordList = CustomText.text;
|
||||
wordList = CustomText.getText();
|
||||
}
|
||||
}
|
||||
const wordset = await Wordset.withWords(wordList);
|
||||
|
|
@ -542,20 +512,14 @@ export async function generateWords(
|
|||
ret.sectionIndexes.push(nextWord.sectionIndex);
|
||||
|
||||
const randomSectionStop =
|
||||
CustomText.isSectionRandom &&
|
||||
CustomText.section !== 0 &&
|
||||
sectionIndex >= CustomText.section;
|
||||
|
||||
const nonRandomSectionStop =
|
||||
!CustomText.isSectionRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
sectionIndex >= wordset.length;
|
||||
CustomText.getLimitMode() === "section" &&
|
||||
CustomText.getLimitValue() !== 0 &&
|
||||
sectionIndex >= CustomText.getLimitValue();
|
||||
|
||||
const customModeStop =
|
||||
Config.mode === "custom" &&
|
||||
currentSection.length === 0 &&
|
||||
CustomText.delimiter === "|" &&
|
||||
(randomSectionStop || nonRandomSectionStop);
|
||||
randomSectionStop;
|
||||
|
||||
if (customModeStop || ret.words.length >= limit) {
|
||||
stop = true;
|
||||
|
|
@ -744,22 +708,19 @@ export async function getNextWord(
|
|||
|
||||
if (Config.mode === "quote") {
|
||||
randomWord = currentQuote[wordIndex] as string;
|
||||
} else if (Config.mode === "custom" && CustomText.getMode() === "repeat") {
|
||||
const customText = CustomText.getText();
|
||||
randomWord = customText[sectionIndex % customText.length] as string;
|
||||
} else if (
|
||||
Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
!CustomText.isSectionRandom
|
||||
) {
|
||||
randomWord = CustomText.text[sectionIndex] as string;
|
||||
} else if (
|
||||
Config.mode === "custom" &&
|
||||
(CustomText.isWordRandom ||
|
||||
CustomText.isTimeRandom ||
|
||||
CustomText.isSectionRandom) &&
|
||||
CustomText.getMode() === "random" &&
|
||||
(wordset.length < 4 || PractiseWords.before.mode !== null)
|
||||
) {
|
||||
randomWord = wordset.randomWord(funboxFrequency);
|
||||
} else if (Config.mode === "custom" && CustomText.isSectionRandom) {
|
||||
} else if (
|
||||
Config.mode === "custom" &&
|
||||
CustomText.getLimitMode() === "section"
|
||||
) {
|
||||
randomWord = wordset.randomWord(funboxFrequency);
|
||||
|
||||
const previousSection = Arrays.nthElementFromArray(sectionHistory, -1);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import * as ConfigEvent from "./observables/config-event";
|
|||
import { debounce, throttle } from "throttle-debounce";
|
||||
import * as TestUI from "./test/test-ui";
|
||||
import { get as getActivePage } from "./states/active-page";
|
||||
import { isDevEnvironment } from "./utils/misc";
|
||||
import { canQuickRestart, isDevEnvironment } from "./utils/misc";
|
||||
import { isCustomTextLong } from "./states/custom-text-name";
|
||||
|
||||
let isPreviewingFont = false;
|
||||
export function previewFontFamily(font: string): void {
|
||||
|
|
@ -56,18 +57,13 @@ window.addEventListener("keydown", function (e) {
|
|||
window.addEventListener("beforeunload", (event) => {
|
||||
// Cancel the event as stated by the standard.
|
||||
if (
|
||||
(Config.mode === "words" && Config.words < 1000) ||
|
||||
(Config.mode === "time" && Config.time < 3600) ||
|
||||
Config.mode === "quote" ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isWordRandom &&
|
||||
CustomText.word < 1000) ||
|
||||
(Config.mode === "custom" &&
|
||||
CustomText.isTimeRandom &&
|
||||
CustomText.time < 1000) ||
|
||||
(Config.mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
CustomText.text.length < 1000)
|
||||
canQuickRestart(
|
||||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
CustomText.getData(),
|
||||
isCustomTextLong() ?? false
|
||||
)
|
||||
) {
|
||||
//ignore
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -221,29 +221,28 @@ export function canQuickRestart(
|
|||
mode: string,
|
||||
words: number,
|
||||
time: number,
|
||||
CustomText: SharedTypes.CustomText,
|
||||
CustomText: SharedTypes.CustomTextData,
|
||||
customTextIsLong: boolean
|
||||
): boolean {
|
||||
const wordsLong = mode === "words" && (words >= 1000 || words === 0);
|
||||
const timeLong = mode === "time" && (time >= 900 || time === 0);
|
||||
const customTextLong = mode === "custom" && customTextIsLong;
|
||||
|
||||
const customTextRandomWordsLong =
|
||||
mode === "custom" && CustomText.isWordRandom && CustomText.word >= 1000;
|
||||
const customTextRandomTimeLong =
|
||||
mode === "custom" && CustomText.isTimeRandom && CustomText.time > 900;
|
||||
const customTextNoRandomLong =
|
||||
mode === "custom" &&
|
||||
!CustomText.isWordRandom &&
|
||||
!CustomText.isTimeRandom &&
|
||||
CustomText.text.length >= 1000;
|
||||
(CustomText.limit.mode === "word" || CustomText.limit.mode === "section") &&
|
||||
(CustomText.limit.value >= 1000 || CustomText.limit.value === 0);
|
||||
const customTextRandomTimeLong =
|
||||
mode === "custom" &&
|
||||
CustomText.limit.mode === "time" &&
|
||||
(CustomText.limit.value >= 900 || CustomText.limit.value === 0);
|
||||
|
||||
if (
|
||||
wordsLong ||
|
||||
timeLong ||
|
||||
customTextLong ||
|
||||
customTextRandomWordsLong ||
|
||||
customTextRandomTimeLong ||
|
||||
customTextNoRandomLong
|
||||
customTextRandomTimeLong
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
|
|||
type SharedTestSettings = [
|
||||
SharedTypes.Config.Mode | null,
|
||||
SharedTypes.Config.Mode2<SharedTypes.Config.Mode> | null,
|
||||
SharedTypes.CustomText | null,
|
||||
SharedTypes.CustomTextData | null,
|
||||
boolean | null,
|
||||
boolean | null,
|
||||
string | null,
|
||||
|
|
@ -147,15 +147,10 @@ export function loadTestSettingsFromUrl(getOverride?: string): void {
|
|||
if (de[2] !== null) {
|
||||
const customTextSettings = de[2];
|
||||
CustomText.setText(customTextSettings.text);
|
||||
CustomText.setIsTimeRandom(customTextSettings.isTimeRandom);
|
||||
CustomText.setIsWordRandom(customTextSettings.isWordRandom);
|
||||
if (customTextSettings.isTimeRandom) {
|
||||
CustomText.setTime(customTextSettings.time);
|
||||
}
|
||||
if (customTextSettings.isWordRandom) {
|
||||
CustomText.setWord(customTextSettings.word);
|
||||
}
|
||||
CustomText.setDelimiter(customTextSettings.delimiter);
|
||||
CustomText.setLimitMode(customTextSettings.limit.mode);
|
||||
CustomText.setLimitValue(customTextSettings.limit.value);
|
||||
CustomText.setPipeDelimiter(customTextSettings.pipeDelimiter);
|
||||
|
||||
applied["custom text settings"] = "";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,14 +85,14 @@
|
|||
"display": "True Simp",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["miodec",true,10000]
|
||||
"parameters": ["miodec", "repeat", 10000, "word", false]
|
||||
}
|
||||
,{
|
||||
"name": "bigramSalad",
|
||||
"display": "Bigram Salad",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["to of in it is as at be we he so on an or do if up by my go",true,100],
|
||||
"parameters": ["to of in it is as at be we he so on an or do if up by my go", "random", 100, "word", false],
|
||||
"requirements" : {
|
||||
"wpm": {
|
||||
"min": 100
|
||||
|
|
@ -104,14 +104,14 @@
|
|||
"display": "Simp",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["miodec",true,1000]
|
||||
"parameters": ["miodec", "repeat", 1000, "word", false]
|
||||
}
|
||||
,{
|
||||
"name": "antidiseWhat",
|
||||
"display": "Antidise-what?",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["antidisestablishmentarianism",true,1],
|
||||
"parameters": ["antidisestablishmentarianism","repeat",1,"word",false],
|
||||
"requirements" : {
|
||||
"wpm": {
|
||||
"min": 200
|
||||
|
|
@ -123,14 +123,14 @@
|
|||
"display": "What's this website called again?",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["monkeytype",true,1000]
|
||||
"parameters": ["monkeytype","repeat",1000, "word", false]
|
||||
}
|
||||
,{
|
||||
"name": "developd",
|
||||
"display": "Develop'd",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["develop",true,1000]
|
||||
"parameters": ["develop","repeat",1000,"word",false]
|
||||
}
|
||||
,{
|
||||
"name": "slowAndSteady",
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
"display": "Speed Spacer",
|
||||
"autoRole": true,
|
||||
"type": "customText",
|
||||
"parameters": ["a b c d e f g h i j k l m n o p q r s t u v w x y z",true,100],
|
||||
"parameters": ["a b c d e f g h i j k l m n o p q r s t u v w x y z","random",100,"word",false],
|
||||
"requirements" : {
|
||||
"wpm": {
|
||||
"min": 100
|
||||
|
|
|
|||
33
shared-types/types.d.ts
vendored
33
shared-types/types.d.ts
vendored
|
|
@ -183,16 +183,6 @@ declare namespace SharedTypes {
|
|||
punctuation: boolean;
|
||||
}
|
||||
|
||||
interface CustomText {
|
||||
text: string[];
|
||||
isWordRandom: boolean;
|
||||
isTimeRandom: boolean;
|
||||
word: number;
|
||||
time: number;
|
||||
delimiter: string;
|
||||
textLen?: number;
|
||||
}
|
||||
|
||||
type DBResult<T extends SharedTypes.Config.Mode> = Omit<
|
||||
SharedTypes.Result<T>,
|
||||
| "bailedOut"
|
||||
|
|
@ -211,6 +201,7 @@ declare namespace SharedTypes {
|
|||
| "customText"
|
||||
| "quoteLength"
|
||||
| "isPb"
|
||||
| "customText"
|
||||
> & {
|
||||
correctChars?: number; // --------------
|
||||
incorrectChars?: number; // legacy results
|
||||
|
|
@ -229,7 +220,7 @@ declare namespace SharedTypes {
|
|||
incompleteTestSeconds?: number;
|
||||
afkDuration?: number;
|
||||
tags?: string[];
|
||||
customText?: CustomText;
|
||||
customText?: CustomTextDataWithTextLen;
|
||||
quoteLength?: number;
|
||||
isPb?: boolean;
|
||||
};
|
||||
|
|
@ -237,7 +228,7 @@ declare namespace SharedTypes {
|
|||
interface CompletedEvent extends Result<SharedTypes.Config.Mode> {
|
||||
keySpacing: number[] | "toolong";
|
||||
keyDuration: number[] | "toolong";
|
||||
customText?: CustomText;
|
||||
customText?: CustomTextDataWithTextLen;
|
||||
wpmConsistency: number;
|
||||
challenge?: string | null;
|
||||
keyOverlap: number;
|
||||
|
|
@ -248,6 +239,24 @@ declare namespace SharedTypes {
|
|||
hash?: string;
|
||||
}
|
||||
|
||||
type CustomTextMode = "repeat" | "random";
|
||||
type CustomTextLimitMode = "word" | "time" | "section";
|
||||
type CustomTextLimit = {
|
||||
value: number;
|
||||
mode: CustomTextLimitMode;
|
||||
};
|
||||
|
||||
type CustomTextData = {
|
||||
text: string[];
|
||||
mode: CustomTextMode;
|
||||
limit: CustomTextLimit;
|
||||
pipeDelimiter: boolean;
|
||||
};
|
||||
|
||||
type CustomTextDataWithTextLen = Omit<CustomTextData, "text"> & {
|
||||
textLen: number;
|
||||
};
|
||||
|
||||
interface ResultFilters {
|
||||
_id: string;
|
||||
name: string;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue