diff --git a/src/index.ts b/src/index.ts
index 2949be5..7e2883a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -31,7 +31,7 @@ export default defineInstrument({
}
translator.init();
- translator.changeLanguage(settingsParseResult.data.language as Language);
+ translator.changeLanguage(settingsParseResult.data.language);
await pictureNamingTask(done);
},
},
diff --git a/src/main.ts b/src/main.ts
index 61de61f..1ee5e91 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,3 @@
-import type { Language } from "@opendatacapture/runtime-v1/@opendatacapture/runtime-core/index.js";
import { experimentSettingsJson } from "./experimentSettings.ts";
import { pictureNamingTask } from "./pictureNamingTask";
@@ -17,6 +16,6 @@ if (!settingsParseResult.success) {
}
translator.init();
-translator.changeLanguage(settingsParseResult.data.language as Language);
+translator.changeLanguage(settingsParseResult.data.language);
await pictureNamingTask();
diff --git a/src/pictureNamingTask.ts b/src/pictureNamingTask.ts
index 74236e6..523e384 100644
--- a/src/pictureNamingTask.ts
+++ b/src/pictureNamingTask.ts
@@ -100,6 +100,9 @@ experimentStimuli
function getRandomElementWithSeed(
array: ExperimentImage[],
): ExperimentImage[] {
+ if (array.length === 0) {
+ throw new Error("no stimuli available for the requested difficulty/language");
+ }
let randomIndex: number;
let foundUnique = false;
@@ -153,6 +156,19 @@ experimentStimuli
jsPsych.pluginAPI.keyUp(key);
}
+ // returns on_start/on_finish hooks that share one listener reference so it can actually be removed
+ function clickAsKeyPress(jsPsych: JsPsych) {
+ const handleClick = () => simulateKeyPress(jsPsych, "a");
+ return {
+ on_start: function () {
+ document.addEventListener("click", handleClick, { once: true });
+ },
+ on_finish: function () {
+ document.removeEventListener("click", handleClick);
+ },
+ };
+ }
+
//****************************
//********EXPERIMENT**********
//****************************
@@ -186,14 +202,7 @@ experimentStimuli
});
const welcome = {
- on_start: function () {
- const handleClick = () => simulateKeyPress(jsPsych, "a");
- document.addEventListener("click", handleClick, { once: true });
- },
- on_finish: function () {
- const handleClick = () => simulateKeyPress(jsPsych, "a");
- document.removeEventListener("click", handleClick);
- },
+ ...clickAsKeyPress(jsPsych),
stimulus: translator.t("welcome"),
type: HtmlKeyboardResponsePlugin,
};
@@ -261,14 +270,7 @@ experimentStimuli
type: HtmlKeyboardResponsePlugin,
};
const showImg = {
- on_start: function () {
- const handleClick = () => simulateKeyPress(jsPsych, "a");
- document.addEventListener("click", handleClick, { once: true });
- },
- on_finish: function () {
- const handleClick = () => simulateKeyPress(jsPsych, "a");
- document.removeEventListener("click", handleClick);
- },
+ ...clickAsKeyPress(jsPsych),
stimulus: jsPsych.timelineVariable("stimulus"),
stimulus_height: 600,
type: ImageKeyboardResponsePlugin,
@@ -298,18 +300,8 @@ experimentStimuli
const html = `
${translator.t("logResponse")}
-
-
+
+
@@ -330,6 +322,7 @@ experimentStimuli
},
};
jsPsych.finishTrial(trialData);
+ return;
}
const submitButton = document.getElementById(
"jspsych-survey-html-form-next",
@@ -337,14 +330,22 @@ experimentStimuli
const resultOutput = document.getElementById(
"result",
) as HTMLOutputElement;
+ const resultAsNumberInput = document.getElementById(
+ "resultAsNumber",
+ ) as HTMLInputElement;
submitButton.disabled = true;
- document.querySelectorAll('input[type="button"]').forEach((button) => {
- button.addEventListener("click", () => {
- if (resultOutput.value) {
+ document
+ .querySelectorAll(
+ "input[type='button'][data-result-as-number]",
+ )
+ .forEach((button) => {
+ button.addEventListener("click", () => {
+ resultOutput.value = button.value;
+ resultOutput.style.color = button.dataset.color ?? "";
+ resultAsNumberInput.value = button.dataset.resultAsNumber ?? "";
submitButton.disabled = false;
- }
+ });
});
- });
},
preamble: function () {
const html = `${translator.t("correctResponse")}
@@ -410,16 +411,16 @@ experimentStimuli
const lastTrialResults: ParticipantResponse =
loggingResponseArray[lastTrialIndex]!.response;
- if (lastTrialResults.result === "Correct") {
+ if (lastTrialResults.resultAsNumber === "1") {
numberOfCorrectAnswers++;
clearSet = false;
- } else if (lastTrialResults.result === "Incorrect") {
+ } else if (lastTrialResults.resultAsNumber === "0") {
numberOfCorrectAnswers = 0;
}
// difficulty level logic
if (numberOfCorrectAnswers === advancementSchedule) {
- if (numberOfCorrectAnswers <= numberOfLevels) {
+ if (currentDifficultyLevel < numberOfLevels) {
currentDifficultyLevel++;
numberOfCorrectAnswers = 0;
clearSet = true;
diff --git a/src/schemas.ts b/src/schemas.ts
index 40550fb..e552302 100644
--- a/src/schemas.ts
+++ b/src/schemas.ts
@@ -44,22 +44,31 @@ export const $ExperimentResultsUnion = z.union([
$ExperimentResults,
$ParticipantIDResult,
]);
-export const $Settings = z.object({
- advancementSchedule: z.coerce.number().positive().int(),
- downloadOnFinish: z.coerce.boolean(),
- initialDifficulty: z.coerce.number().positive().int(),
- language: z.string(),
- numberOfLevels: z.coerce.number().positive().int(),
- regressionSchedule: z.coerce.number().int(),
- seed: z.preprocess(
- (val) =>
- val === "" || val === null || val === 0 || val === undefined
- ? undefined
- : val,
- z.coerce.number().positive().int().optional(),
- ),
- totalNumberOfTrialsToRun: z.coerce.number().positive().int(),
-});
+export const $Settings = z
+ .object({
+ advancementSchedule: z.coerce.number().positive().int(),
+ // z.coerce.boolean() would turn the string "false" into true
+ downloadOnFinish: z.preprocess(
+ (val) => (val === "false" ? false : val === "true" ? true : val),
+ z.boolean(),
+ ),
+ initialDifficulty: z.coerce.number().positive().int(),
+ language: $Language,
+ numberOfLevels: z.coerce.number().positive().int(),
+ regressionSchedule: z.coerce.number().nonnegative().int(),
+ seed: z.preprocess(
+ (val) =>
+ val === "" || val === null || val === 0 || val === undefined
+ ? undefined
+ : val,
+ z.coerce.number().positive().int().optional(),
+ ),
+ totalNumberOfTrialsToRun: z.coerce.number().positive().int(),
+ })
+ .refine((settings) => settings.initialDifficulty <= settings.numberOfLevels, {
+ message: "initialDifficulty must not exceed numberOfLevels",
+ path: ["initialDifficulty"],
+ });
export const $ExperimentImage = z.object({
correctResponse: z.string(),