diff --git a/task-launcher/src/tasks/mental-rotation/helpers/config.ts b/task-launcher/src/tasks/mental-rotation/helpers/config.ts index 23705888b..4fbe4dd0b 100644 --- a/task-launcher/src/tasks/mental-rotation/helpers/config.ts +++ b/task-launcher/src/tasks/mental-rotation/helpers/config.ts @@ -5,6 +5,7 @@ import { DEFAULT_LAYOUT_CONFIG, mapDistractorsToString, } from '../../shared/helpers'; +import { taskStore } from '../../../taskStore'; type GetConfigReturnType = { itemConfig: LayoutConfigType; @@ -40,6 +41,10 @@ export const getLayoutConfig = ( defaultConfig.classOverrides.buttonClassList = ['primary']; } + if (taskStore().heavyInstructions && defaultConfig.isPracticeTrial) { + defaultConfig.heavyPracticeStaggered = true; + } + const messages = validateLayoutConfig(defaultConfig, mediaAssets, translations, stimulus); return { diff --git a/task-launcher/src/tasks/mental-rotation/timeline.ts b/task-launcher/src/tasks/mental-rotation/timeline.ts index 27f95c65f..6a30d9e4a 100644 --- a/task-launcher/src/tasks/mental-rotation/timeline.ts +++ b/task-launcher/src/tasks/mental-rotation/timeline.ts @@ -8,6 +8,9 @@ import { threeDimInstructions, videoInstructionsFit, videoInstructionsMisfit, + demo1, + demo2, + encouragement } from './trials/instructions'; import { afcStimulusTemplate, @@ -27,9 +30,9 @@ import { taskStore } from '../../taskStore'; export default function buildMentalRotationTimeline(config: Record, mediaAssets: MediaAssetsType) { const preloadTrials = createPreloadTrials(mediaAssets).default; - const { runCat } = taskStore(); - const { semThreshold } = taskStore(); + const { runCat, semThreshold, heavyInstructions } = taskStore(); let playedThreeDimInstructions = false; + let encouragementCounter = 0; // only used for younger kid version (heavy instructions) initTrialSaving(config); const initialTimeline = initTimeline(config, enterFullscreen, finishExperiment); @@ -50,7 +53,17 @@ export default function buildMentalRotationTimeline(config: Record, }, }; - const timeline = [preloadTrials, initialTimeline, imageInstructions, videoInstructionsMisfit, videoInstructionsFit]; + const timeline = [ + preloadTrials, + initialTimeline, + imageInstructions, + videoInstructionsMisfit, + videoInstructionsFit, + ]; + + if (heavyInstructions) { + timeline.push(demo1); + } const corpus: StimulusType[] = taskStore().corpora.stimulus; const translations: Record = taskStore().translations; const validationErrorMap: Record = {}; @@ -105,7 +118,9 @@ export default function buildMentalRotationTimeline(config: Record, }; const threeDimInstructBlock = { - timeline: [threeDimInstructions], + timeline: [ + (heavyInstructions ? demo2 : threeDimInstructions) + ], conditional_function: () => { if (taskStore().nextStimulus.trialType === '3D' && !playedThreeDimInstructions) { playedThreeDimInstructions = true; @@ -139,12 +154,22 @@ export default function buildMentalRotationTimeline(config: Record, const numOfCatTrials = corpora.cat.length; taskStore('totalTestTrials', numOfCatTrials); for (let i = 0; i < numOfCatTrials; i++) { + if (heavyInstructions) { + encouragementCounter ++ + }; + if (i === 2) { timeline.push(repeatInstructions); } timeline.push({ ...setupStimulus, stimulus: '' }); timeline.push(threeDimInstructBlock); timeline.push(stimulusBlock); + + // add encouragement at variable interval + if ((encouragementCounter > 5) && Math.random() < 0.25 && heavyInstructions) { + timeline.push(encouragement); + encouragementCounter = 0; + } } const unnormedTrials: StimulusType[] = selectNItems(corpora.unnormed, 5); @@ -158,6 +183,10 @@ export default function buildMentalRotationTimeline(config: Record, const numOfTrials = taskStore().totalTrials; taskStore('totalTestTrials', getRealTrials(corpus)); for (let i = 0; i < numOfTrials; i++) { + if (heavyInstructions) { + encouragementCounter ++ + }; + if (i === 4) { timeline.push(repeatInstructions); } @@ -165,6 +194,11 @@ export default function buildMentalRotationTimeline(config: Record, timeline.push(practiceTransition); timeline.push(threeDimInstructBlock); timeline.push(stimulusBlock); + + if ((encouragementCounter > 4) && Math.random() < 0.3 && heavyInstructions) { + timeline.push(encouragement); + encouragementCounter = 0; + } } } diff --git a/task-launcher/src/tasks/mental-rotation/trials/instructions.ts b/task-launcher/src/tasks/mental-rotation/trials/instructions.ts index a5bdfed73..12bb93af4 100644 --- a/task-launcher/src/tasks/mental-rotation/trials/instructions.ts +++ b/task-launcher/src/tasks/mental-rotation/trials/instructions.ts @@ -21,54 +21,80 @@ const audioConfig: AudioConfigType = { onEnded: enableOkBtn, }; -// Switch to HTMLMultiResponse when we have video with audio -export const videoInstructionsFit = { - type: jsPsychHtmlMultiResponse, - data: () => { - return { - // save_trial: true, - assessment_stage: 'instructions', - }; - }, - stimulus: () => { - return ` -
- - -
- `; - }, - prompt_above_buttons: true, - button_choices: ['Continue'], - button_html: () => { - const t = taskStore().translations; - return ``; - }, - keyboard_choices: 'NO_KEYS', - on_load: () => { - // const wrapper = document.getElementById('jspsych-audio-multi-response-prompt'); - // wrapper.style.display = 'flex'; - // wrapper.style.justifyContent = 'center'; - PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationTrainingInstruct3, audioConfig); +const videoInstructionsData = [ + { + audio: 'mentalRotationTrainingInstruct2', + stimulus: 'exampleMisfitTest', + loop: false, + }, + { + audio: 'mentalRotationTrainingInstruct3', + stimulus: 'exampleFitTest', + loop: false, + }, + { + audio: 'mentalRotationDemo1Heavy', + stimulus: 'mentalRotationDemo1', + loop: true, + }, + { + audio: 'mentalRotationDemo2Heavy', + stimulus: 'mentalRotationDemo2', + loop: true, + }, +]; - const pageStateHandler = new PageStateHandler('mental-rotation-training-instruct3', true); - setupReplayAudio(pageStateHandler); - }, - on_finish: () => { - PageAudioHandler.stopAndDisconnectNode(); +const videoInstructions = videoInstructionsData.map((data) => { + return { + type: jsPsychHtmlMultiResponse, + data: () => { + return { + // save_trial: true, + assessment_stage: 'instructions', + }; + }, + stimulus: () => { + return ` +
+ + +
+ `; + }, + prompt_above_buttons: true, + button_choices: ['Continue'], + button_html: () => { + const t = taskStore().translations; + return ``; + }, + keyboard_choices: 'NO_KEYS', + on_load: () => { + PageAudioHandler.playAudio(mediaAssets.audio[data.audio], audioConfig); - jsPsych.data.addDataToLastTrial({ - audioButtonPresses: PageAudioHandler.replayPresses, - }); - }, -}; + const pageStateHandler = new PageStateHandler(data.audio, true); + setupReplayAudio(pageStateHandler); + }, + on_finish: () => { + PageAudioHandler.stopAndDisconnectNode(); + + jsPsych.data.addDataToLastTrial({ + audioButtonPresses: PageAudioHandler.replayPresses, + }); + }, + } +}); + +export const videoInstructionsFit = videoInstructions[0]; +export const videoInstructionsMisfit = videoInstructions[1]; +export const demo1 = videoInstructions[2]; +export const demo2 = videoInstructions[3]; -export const videoInstructionsMisfit = { +export const imageInstructions = { type: jsPsychHtmlMultiResponse, data: () => { return { @@ -81,10 +107,7 @@ export const videoInstructionsMisfit = { - + `; }, @@ -92,13 +115,13 @@ export const videoInstructionsMisfit = { button_choices: ['Continue'], button_html: () => { const t = taskStore().translations; - return ``; + return ``; }, keyboard_choices: 'NO_KEYS', on_load: () => { - PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationTrainingInstruct2, audioConfig); + PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationInstruct1, audioConfig); - const pageStateHandler = new PageStateHandler('mental-rotation-training-instruct2', true); + const pageStateHandler = new PageStateHandler('mental-rotation-instruct1', true); setupReplayAudio(pageStateHandler); }, on_finish: () => { @@ -110,7 +133,7 @@ export const videoInstructionsMisfit = { }, }; -export const imageInstructions = { +export const threeDimInstructions = { type: jsPsychHtmlMultiResponse, data: () => { return { @@ -118,38 +141,42 @@ export const imageInstructions = { }; }, stimulus: () => { + const t = taskStore().translations; return `
+
+

${t.mentalRotationInstruct3D}

+
-
`; }, prompt_above_buttons: true, button_choices: ['Continue'], + post_trial_gap: 350, button_html: () => { const t = taskStore().translations; - return ``; + return ``; }, keyboard_choices: 'NO_KEYS', + trial_ends_after_audio: false, + response_allowed_while_playing: false, on_load: () => { - PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationInstruct1, audioConfig); - - const pageStateHandler = new PageStateHandler('mental-rotation-instruct1', true); + PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationInstruct3D); + const pageStateHandler = new PageStateHandler('mental-rotation-instruct-3D', true); setupReplayAudio(pageStateHandler); }, on_finish: () => { PageAudioHandler.stopAndDisconnectNode(); - jsPsych.data.addDataToLastTrial({ audioButtonPresses: PageAudioHandler.replayPresses, }); }, }; -export const threeDimInstructions = { +export const encouragement = { type: jsPsychHtmlMultiResponse, data: () => { return { @@ -161,30 +188,26 @@ export const threeDimInstructions = { return `
-

${t.mentalRotationInstruct3D}

+

${t.generalEncourage}

+
`; }, prompt_above_buttons: true, - button_choices: ['Continue'], + trial_duration: 1500, post_trial_gap: 350, - button_html: () => { - const t = taskStore().translations; - return ``; - }, keyboard_choices: 'NO_KEYS', - trial_ends_after_audio: false, - response_allowed_while_playing: false, on_load: () => { - PageAudioHandler.playAudio(mediaAssets.audio.mentalRotationInstruct3D); - const pageStateHandler = new PageStateHandler('mental-rotation-instruct-3D', true); + PageAudioHandler.playAudio(mediaAssets.audio.generalEncourage); + const pageStateHandler = new PageStateHandler('general-encourage', true); setupReplayAudio(pageStateHandler); }, on_finish: () => { + PageAudioHandler.stopAndDisconnectNode(); jsPsych.data.addDataToLastTrial({ audioButtonPresses: PageAudioHandler.replayPresses, }); diff --git a/task-launcher/src/tasks/shared/helpers/config.ts b/task-launcher/src/tasks/shared/helpers/config.ts index fc293a415..31ba498aa 100644 --- a/task-launcher/src/tasks/shared/helpers/config.ts +++ b/task-launcher/src/tasks/shared/helpers/config.ts @@ -32,6 +32,7 @@ export const DEFAULT_LAYOUT_CONFIG: LayoutConfigType = { isInstructionTrial: false, randomizeChoiceOrder: false, isStaggered: false, + heavyPracticeStaggered: false, isImageButtonResponse: false, showStimImage: true, response: { diff --git a/task-launcher/src/tasks/shared/trials/afcStimulus.ts b/task-launcher/src/tasks/shared/trials/afcStimulus.ts index a8bf8211d..eb196ddae 100644 --- a/task-launcher/src/tasks/shared/trials/afcStimulus.ts +++ b/task-launcher/src/tasks/shared/trials/afcStimulus.ts @@ -184,7 +184,8 @@ function getButtonChoices(layoutConfigMap: Record, tri function getButtonHtml(layoutConfigMap: Record, trial?: StimulusType) { const stimulus = trial || taskStore().nextStimulus; - const isPracticeTrial = stimulus.assessmentStage === 'practice_response'; + //const isPracticeTrial = stimulus.assessmentStage === 'practice_response'; + const isPracticeTrial = true; const itemLayoutConfig = layoutConfigMap?.[stimulus.itemId]; if (itemLayoutConfig) { const classList = [...itemLayoutConfig.classOverrides.buttonClassList]; @@ -229,17 +230,21 @@ function doOnLoad(layoutConfigMap: Record, trial?: Sti const isPracticeTrial = stim.assessmentStage === 'practice_response'; const isInstructionTrial = stim.trialType === 'instructions'; - if (itemLayoutConfig.isStaggered) { + if (itemLayoutConfig.isStaggered || itemLayoutConfig.heavyPracticeStaggered) { // Handle the staggered buttons const buttonContainer = document.getElementById('jspsych-html-multi-response-btngroup') as HTMLDivElement; const imgButtons = Array.from(buttonContainer.children as HTMLCollectionOf); let audioKeys: string[] = []; - for (let i = 0; i < imgButtons.length; i++) { - const img = imgButtons[i].children[0].getElementsByTagName('img')[0]; - const audioKey = camelize(img?.alt ?? ''); - audioKeys.push(audioKey); + if (itemLayoutConfig.heavyPracticeStaggered) { + audioKeys = ['same-different-selection-highlight-1', 'same-different-selection-highlight-2']; + } else { + for (let i = 0; i < imgButtons.length; i++) { + const img = imgButtons[i].children[0].getElementsByTagName('img')[0]; + const audioKey = camelize(img?.alt ?? ''); + audioKeys.push(audioKey); + } } - + handleStaggeredButtons(pageStateHandler, buttonContainer, audioKeys); } diff --git a/task-launcher/types/index.d.ts b/task-launcher/types/index.d.ts index 351695d63..7a589a8d3 100644 --- a/task-launcher/types/index.d.ts +++ b/task-launcher/types/index.d.ts @@ -27,6 +27,7 @@ declare global { isInstructionTrial: boolean; randomizeChoiceOrder: boolean; isStaggered: boolean; + heavyPracticeStaggered?: boolean, isImageButtonResponse: boolean; showStimImage: boolean; response: {