Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions task-launcher/src/tasks/mental-rotation/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DEFAULT_LAYOUT_CONFIG,
mapDistractorsToString,
} from '../../shared/helpers';
import { taskStore } from '../../../taskStore';

type GetConfigReturnType = {
itemConfig: LayoutConfigType;
Expand Down Expand Up @@ -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 {
Expand Down
42 changes: 38 additions & 4 deletions task-launcher/src/tasks/mental-rotation/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
threeDimInstructions,
videoInstructionsFit,
videoInstructionsMisfit,
demo1,
demo2,
encouragement
} from './trials/instructions';
import {
afcStimulusTemplate,
Expand All @@ -27,9 +30,9 @@ import { taskStore } from '../../taskStore';

export default function buildMentalRotationTimeline(config: Record<string, any>, 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);
Expand All @@ -50,7 +53,17 @@ export default function buildMentalRotationTimeline(config: Record<string, any>,
},
};

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<string, string> = taskStore().translations;
const validationErrorMap: Record<string, string> = {};
Expand Down Expand Up @@ -105,7 +118,9 @@ export default function buildMentalRotationTimeline(config: Record<string, any>,
};

const threeDimInstructBlock = {
timeline: [threeDimInstructions],
timeline: [
(heavyInstructions ? demo2 : threeDimInstructions)
],
conditional_function: () => {
if (taskStore().nextStimulus.trialType === '3D' && !playedThreeDimInstructions) {
playedThreeDimInstructions = true;
Expand Down Expand Up @@ -139,12 +154,22 @@ export default function buildMentalRotationTimeline(config: Record<string, any>,
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);
Expand All @@ -158,13 +183,22 @@ export default function buildMentalRotationTimeline(config: Record<string, any>,
const numOfTrials = taskStore().totalTrials;
taskStore('totalTestTrials', getRealTrials(corpus));
for (let i = 0; i < numOfTrials; i++) {
if (heavyInstructions) {
encouragementCounter ++
};

if (i === 4) {
timeline.push(repeatInstructions);
}
timeline.push({ ...setupStimulus, stimulus: '' });
timeline.push(practiceTransition);
timeline.push(threeDimInstructBlock);
timeline.push(stimulusBlock);

if ((encouragementCounter > 4) && Math.random() < 0.3 && heavyInstructions) {
timeline.push(encouragement);
encouragementCounter = 0;
}
}
}

Expand Down
163 changes: 93 additions & 70 deletions task-launcher/src/tasks/mental-rotation/trials/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 `
<div class="lev-stimulus-container">
<button id="${replayButtonHtmlId}" class="replay">
${replayButtonSvg}
</button>
<video class="instruction-video" autoplay>
<source src=${mediaAssets.video.mentalRotationExampleFit} type="video/mp4"/>
Your browser does not support the video tag.
</video>
</div>
`;
},
prompt_above_buttons: true,
button_choices: ['Continue'],
button_html: () => {
const t = taskStore().translations;
return `<button class="primary" disabled>${t.continueButtonText}</button>`;
},
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 `
<div class="lev-stimulus-container">
<button id="${replayButtonHtmlId}" class="replay">
${replayButtonSvg}
</button>
<video class="instruction-video" autoplay ${data.loop ? 'loop' : ''}>
<source src=${mediaAssets.video[data.stimulus]} type="video/mp4"/>
Your browser does not support the video tag.
</video>
</div>
`;
},
prompt_above_buttons: true,
button_choices: ['Continue'],
button_html: () => {
const t = taskStore().translations;
return `<button class="primary" disabled>${t.continueButtonText}</button>`;
},
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 {
Expand All @@ -81,24 +107,21 @@ export const videoInstructionsMisfit = {
<button id="${replayButtonHtmlId}" class="replay">
${replayButtonSvg}
</button>
<video class="instruction-video" autoplay>
<source src=${mediaAssets.video.mentalRotationExampleMisfit} type="video/mp4"/>
Your browser does not support the video tag.
</video>
<img src=${mediaAssets.images.mentalRotationExample} class="instruction-video" />
</div>
`;
},
prompt_above_buttons: true,
button_choices: ['Continue'],
button_html: () => {
const t = taskStore().translations;
return `<button class="primary" id="ok-button" disabled>${t.continueButtonText}</button>`;
return `<button class="primary" disabled>${t.continueButtonText}</button>`;
},
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: () => {
Expand All @@ -110,46 +133,50 @@ export const videoInstructionsMisfit = {
},
};

export const imageInstructions = {
export const threeDimInstructions = {
type: jsPsychHtmlMultiResponse,
data: () => {
return {
assessment_stage: 'instructions',
};
},
stimulus: () => {
const t = taskStore().translations;
return `
<div class="lev-stimulus-container">
<div class="lev-row-container instruction">
<p>${t.mentalRotationInstruct3D}</p>
</div>
<button id="${replayButtonHtmlId}" class="replay">
${replayButtonSvg}
</button>
<img src=${mediaAssets.images.mentalRotationExample} class="instruction-video" />
</div>
`;
},
prompt_above_buttons: true,
button_choices: ['Continue'],
post_trial_gap: 350,
button_html: () => {
const t = taskStore().translations;
return `<button class="primary" disabled>${t.continueButtonText}</button>`;
return `<button class="primary">${t.continueButtonText}</button>`;
},
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 {
Expand All @@ -161,30 +188,26 @@ export const threeDimInstructions = {
return `
<div class="lev-stimulus-container">
<div class="lev-row-container instruction">
<p>${t.mentalRotationInstruct3D}</p>
<p>${t.generalEncourage}</p>
</div>
<button id="${replayButtonHtmlId}" class="replay">
${replayButtonSvg}
</button>
<img src=${mediaAssets.images['smilingFace@2x']} class="instruction-video" />
</div>
`;
},
prompt_above_buttons: true,
button_choices: ['Continue'],
trial_duration: 1500,
post_trial_gap: 350,
button_html: () => {
const t = taskStore().translations;
return `<button class="primary">${t.continueButtonText}</button>`;
},
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,
});
Expand Down
1 change: 1 addition & 0 deletions task-launcher/src/tasks/shared/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const DEFAULT_LAYOUT_CONFIG: LayoutConfigType = {
isInstructionTrial: false,
randomizeChoiceOrder: false,
isStaggered: false,
heavyPracticeStaggered: false,
isImageButtonResponse: false,
showStimImage: true,
response: {
Expand Down
Loading