diff --git a/packages/plugin-headphone-check/examples/basic-configuration.html b/packages/plugin-headphone-check/examples/basic-configuration.html index 95bbd584..35bd6e95 100644 --- a/packages/plugin-headphone-check/examples/basic-configuration.html +++ b/packages/plugin-headphone-check/examples/basic-configuration.html @@ -21,13 +21,16 @@ auto_preload: true } + // this trial uses the default configuration described in the original headphonecheck + // https://github.com/mcdermottLab/HeadphoneCheck?tab=readme-ov-file#configure-and-start-the-headphone-check + // note sequential + sampleWithReplacement are defined: their default values are false. const trial = { type: jsPsychHeadphoneCheck, stimuli: ["./audio/antiphase_HC_ISO.wav", "./audio/antiphase_HC_IOS.wav", "./audio/antiphase_HC_SOI.wav", "./audio/antiphase_HC_SIO.wav", "./audio/antiphase_HC_OSI.wav", "./audio/antiphase_HC_OIS.wav"], correct: [2, 3, 1, 1, 2, 3], calibration_stimulus: "./audio/noise_calib_stim.wav", sequential: true, - shuffle: false, + sampleWithReplacement: true }; jsPsych.run([preload, trial]) diff --git a/packages/plugin-headphone-check/src/index.ts b/packages/plugin-headphone-check/src/index.ts index b6067b57..d2035df4 100644 --- a/packages/plugin-headphone-check/src/index.ts +++ b/packages/plugin-headphone-check/src/index.ts @@ -167,6 +167,7 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { private stimuliList: string[]; private correctList: number[]; + private trialCounter: number; private currentPage: number; private css: string = @@ -248,22 +249,17 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { this.stimuliList = this.params.stimuli; this.correctList = this.params.correct; if (this.params.shuffle) { + var shuffled: number[]; if (this.params.sample_with_replacement) { - this.stimuliList = this.jsPsych.randomization.sampleWithReplacement( - this.stimuliList, - this.params.total_trials - ); - this.correctList = this.jsPsych.randomization.sampleWithReplacement( - this.correctList, + shuffled = this.jsPsych.randomization.sampleWithReplacement( + [...Array(this.params.total_trials).keys()], this.params.total_trials ); } else { - var shuffled = this.jsPsych.randomization.shuffle([ - ...Array(this.params.total_trials).keys(), - ]); - this.stimuliList = shuffled.map((i) => this.params.stimuli[i]); - this.correctList = shuffled.map((i) => this.params.correct[i]); + shuffled = this.jsPsych.randomization.shuffle([...Array(this.params.total_trials).keys()]); } + this.stimuliList = shuffled.map((i) => this.params.stimuli[i]); + this.correctList = shuffled.map((i) => this.params.correct[i]); } // instantiate trial resources @@ -294,6 +290,11 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { radio.name = `jspsych-headphone-check-radio-${stimuliIndex}`; radio.id = `jspsych-headphone-check-radio-${stimuliIndex}-${labelIndex}`; radio.value = labelIndex.toString(); + if (this.params.sequential) + radio.addEventListener( + "click", + this.handleCheckRadioClick(stimuliIndex % this.params.trials_per_page) + ); var radioLabel = document.createElement("label"); radioLabel.setAttribute("for", radio.id); @@ -399,6 +400,7 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { /** rest of headphone check- similar to calibration this will get re-called */ private async beginCheck() { const currentResources = this.getCurrentResources(); + this.trialCounter = 0; // reset display this.container.innerHTML = ""; @@ -410,6 +412,11 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { for (const resource of currentResources) { fieldsetContainer.appendChild(resource.fieldset); } + + if (this.params.sequential) { + this.handleSequentialFieldsets(); + } + this.container.appendChild(fieldsetContainer); this.container.appendChild(this.trialContinueButton); @@ -435,9 +442,24 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { audio.removeEventListener("ended", this.handleCheckAudioEnd(audio, fieldset)); var fieldsets = document.querySelectorAll(`.${fieldset.className}`); - fieldsets.forEach((fieldset) => { + // not seq, enable all fieldsets, otherwise, enable current, radio will enable next + if (!this.params.sequential) { + fieldsets.forEach((fs) => { + fs.removeAttribute("disabled"); + }); + } else { fieldset.removeAttribute("disabled"); - }); + } + }; + } + + /** enables the next fieldset on answer */ + private handleCheckRadioClick(id: number) { + return () => { + if (id < this.trialCounter || this.trialCounter === this.params.trials_per_page - 1) return; + + this.trialCounter++; + this.handleSequentialFieldsets(); }; } @@ -456,6 +478,17 @@ class HeadphoneCheckPlugin implements JsPsychPlugin { } } + /** enables current fieldset, disables all others */ + private handleSequentialFieldsets() { + var currentResources = this.getCurrentResources(); + var currentFieldset = currentResources[this.trialCounter].fieldset; + currentFieldset.removeAttribute("disabled"); + for (var i = this.trialCounter + 1; i < currentResources.length; i++) { + var fieldset = currentResources[i].fieldset; + fieldset.setAttribute("disabled", "disabled"); + } + } + /** checks for and highlights fieldsets that the participant has not responded to */ private checkData(): boolean { var isValidData = true;