var jsPsychReconstruction = (function (jspsych) { 'use strict'; const info = { name: "reconstruction", parameters: { /** A function with a single parameter that returns an HTML-formatted string representing the stimulus. */ stim_function: { type: jspsych.ParameterType.FUNCTION, pretty_name: "Stimulus function", default: undefined, }, /** The starting value of the stimulus parameter. */ starting_value: { type: jspsych.ParameterType.FLOAT, pretty_name: "Starting value", default: 0.5, }, /** The change in the stimulus parameter caused by pressing one of the modification keys. */ step_size: { type: jspsych.ParameterType.FLOAT, pretty_name: "Step size", default: 0.05, }, /** The key to press for increasing the parameter value. */ key_increase: { type: jspsych.ParameterType.KEY, pretty_name: "Key increase", default: "h", }, /** The key to press for decreasing the parameter value. */ key_decrease: { type: jspsych.ParameterType.KEY, pretty_name: "Key decrease", default: "g", }, /** The text that appears on the button to finish the trial. */ button_label: { type: jspsych.ParameterType.STRING, pretty_name: "Button label", default: "Continue", }, }, }; /** * **reconstruction** * * jsPsych plugin for a reconstruction task where the subject recreates a stimulus from memory * * @author Josh de Leeuw * @see {@link https://www.jspsych.org/plugins/jspsych-reconstruction/ reconstruction plugin documentation on jspsych.org} */ class ReconstructionPlugin { constructor(jsPsych) { this.jsPsych = jsPsych; } trial(display_element, trial) { // current param level var param = trial.starting_value; const endTrial = () => { // measure response time var endTime = performance.now(); var response_time = Math.round(endTime - startTime); // clear keyboard response this.jsPsych.pluginAPI.cancelKeyboardResponse(key_listener); // save data var trial_data = { rt: response_time, final_value: param, start_value: trial.starting_value, }; display_element.innerHTML = ""; // next trial this.jsPsych.finishTrial(trial_data); }; const draw = (param) => { //console.log(param); display_element.innerHTML = '
' + trial.stim_function(param) + "
"; // add submit button display_element.innerHTML += '"; display_element .querySelector("#jspsych-reconstruction-next") .addEventListener("click", endTrial); }; // set-up key listeners const after_response = (info) => { //console.log('fire'); var key_i = trial.key_increase; var key_d = trial.key_decrease; // get new param value if (this.jsPsych.pluginAPI.compareKeys(info.key, key_i)) { param = param + trial.step_size; } else if (this.jsPsych.pluginAPI.compareKeys(info.key, key_d)) { param = param - trial.step_size; } param = Math.max(Math.min(1, param), 0); // refresh the display draw(param); }; // listen for responses var key_listener = this.jsPsych.pluginAPI.getKeyboardResponse({ callback_function: after_response, valid_responses: [trial.key_increase, trial.key_decrease], rt_method: "performance", persist: true, allow_held_key: true, }); // draw first iteration draw(param); var startTime = performance.now(); } simulate(trial, simulation_mode, simulation_options, load_callback) { if (simulation_mode == "data-only") { load_callback(); this.simulate_data_only(trial, simulation_options); } if (simulation_mode == "visual") { this.simulate_visual(trial, simulation_options, load_callback); } } create_simulation_data(trial, simulation_options) { const default_data = { rt: this.jsPsych.randomization.sampleExGaussian(2000, 200, 1 / 200, true), start_value: trial.starting_value, final_value: this.jsPsych.randomization.randomInt(0, Math.round(1 / trial.step_size)) * trial.step_size, }; const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options); this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data); return data; } simulate_data_only(trial, simulation_options) { const data = this.create_simulation_data(trial, simulation_options); this.jsPsych.finishTrial(data); } simulate_visual(trial, simulation_options, load_callback) { const data = this.create_simulation_data(trial, simulation_options); const display_element = this.jsPsych.getDisplayElement(); this.trial(display_element, trial); load_callback(); let steps = Math.round((data.final_value - trial.starting_value) / trial.step_size); const rt_per_step = (data.rt - 300) / steps; let t = 0; while (steps != 0) { if (steps > 0) { this.jsPsych.pluginAPI.pressKey(trial.key_increase, t + rt_per_step); steps--; } else { this.jsPsych.pluginAPI.pressKey(trial.key_decrease, t + rt_per_step); steps++; } t += rt_per_step; } this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#jspsych-reconstruction-next"), data.rt); } } ReconstructionPlugin.info = info; return ReconstructionPlugin; })(jsPsychModule);