var jsPsychInstructions = (function (jspsych) { 'use strict'; const info = { name: "instructions", parameters: { /** Each element of the array is the HTML-formatted content for a single page. */ pages: { type: jspsych.ParameterType.HTML_STRING, pretty_name: "Pages", default: undefined, array: true, }, /** The key the subject can press in order to advance to the next page. */ key_forward: { type: jspsych.ParameterType.KEY, pretty_name: "Key forward", default: "ArrowRight", }, /** The key that the subject can press to return to the previous page. */ key_backward: { type: jspsych.ParameterType.KEY, pretty_name: "Key backward", default: "ArrowLeft", }, /** If true, the subject can return to the previous page of the instructions. */ allow_backward: { type: jspsych.ParameterType.BOOL, pretty_name: "Allow backward", default: true, }, /** If true, the subject can use keyboard keys to navigate the pages. */ allow_keys: { type: jspsych.ParameterType.BOOL, pretty_name: "Allow keys", default: true, }, /** If true, then a "Previous" and "Next" button will be displayed beneath the instructions. */ show_clickable_nav: { type: jspsych.ParameterType.BOOL, pretty_name: "Show clickable nav", default: false, }, /** If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons. */ show_page_number: { type: jspsych.ParameterType.BOOL, pretty_name: "Show page number", default: false, }, /** The text that appears before x/y (current/total) pages displayed with show_page_number. */ page_label: { type: jspsych.ParameterType.STRING, pretty_name: "Page label", default: "Page", }, /** The text that appears on the button to go backwards. */ button_label_previous: { type: jspsych.ParameterType.STRING, pretty_name: "Button label previous", default: "Previous", }, /** The text that appears on the button to go forwards. */ button_label_next: { type: jspsych.ParameterType.STRING, pretty_name: "Button label next", default: "Next", }, }, }; /** * **instructions** * * jsPsych plugin to display text (including HTML-formatted strings) during the experiment. * Use it to show a set of pages that participants can move forward/backward through. * Page numbers can be displayed to help with navigation by setting show_page_number to true. * * @author Josh de Leeuw * @see {@link https://www.jspsych.org/plugins/jspsych-instructions/ instructions plugin documentation on jspsych.org} */ class InstructionsPlugin { constructor(jsPsych) { this.jsPsych = jsPsych; } trial(display_element, trial) { var current_page = 0; var view_history = []; var start_time = performance.now(); var last_page_update_time = start_time; function btnListener(evt) { evt.target.removeEventListener("click", btnListener); if (this.id === "jspsych-instructions-back") { back(); } else if (this.id === "jspsych-instructions-next") { next(); } } function show_current_page() { var html = trial.pages[current_page]; var pagenum_display = ""; if (trial.show_page_number) { pagenum_display = "" + trial.page_label + " " + (current_page + 1) + "/" + trial.pages.length + ""; } if (trial.show_clickable_nav) { var nav_html = "
"; if (trial.allow_backward) { var allowed = current_page > 0 ? "" : "disabled='disabled'"; nav_html += ""; } if (trial.pages.length > 1 && trial.show_page_number) { nav_html += pagenum_display; } nav_html += "
"; html += nav_html; display_element.innerHTML = html; if (current_page != 0 && trial.allow_backward) { display_element .querySelector("#jspsych-instructions-back") .addEventListener("click", btnListener); } display_element .querySelector("#jspsych-instructions-next") .addEventListener("click", btnListener); } else { if (trial.show_page_number && trial.pages.length > 1) { // page numbers for non-mouse navigation html += "
" + pagenum_display + "
"; } display_element.innerHTML = html; } } function next() { add_current_page_to_view_history(); current_page++; // if done, finish up... if (current_page >= trial.pages.length) { endTrial(); } else { show_current_page(); } } function back() { add_current_page_to_view_history(); current_page--; show_current_page(); } function add_current_page_to_view_history() { var current_time = performance.now(); var page_view_time = Math.round(current_time - last_page_update_time); view_history.push({ page_index: current_page, viewing_time: page_view_time, }); last_page_update_time = current_time; } const endTrial = () => { if (trial.allow_keys) { this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener); } display_element.innerHTML = ""; var trial_data = { view_history: view_history, rt: Math.round(performance.now() - start_time), }; this.jsPsych.finishTrial(trial_data); }; const after_response = (info) => { // have to reinitialize this instead of letting it persist to prevent accidental skips of pages by holding down keys too long keyboard_listener = this.jsPsych.pluginAPI.getKeyboardResponse({ callback_function: after_response, valid_responses: [trial.key_forward, trial.key_backward], rt_method: "performance", persist: false, allow_held_key: false, }); // check if key is forwards or backwards and update page if (this.jsPsych.pluginAPI.compareKeys(info.key, trial.key_backward)) { if (current_page !== 0 && trial.allow_backward) { back(); } } if (this.jsPsych.pluginAPI.compareKeys(info.key, trial.key_forward)) { next(); } }; show_current_page(); if (trial.allow_keys) { var keyboard_listener = this.jsPsych.pluginAPI.getKeyboardResponse({ callback_function: after_response, valid_responses: [trial.key_forward, trial.key_backward], rt_method: "performance", persist: false, }); } } 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) { let curr_page = 0; let rt = 0; const view_history = []; while (curr_page !== trial.pages.length) { const view_time = this.jsPsych.randomization.sampleExGaussian(3000, 300, 1 / 300); view_history.push({ page_index: curr_page, viewing_time: view_time }); rt += view_time; if (curr_page == 0 || !trial.allow_backward) { curr_page++; } else { if (this.jsPsych.randomization.sampleBernoulli(0.9) == 1) { curr_page++; } else { curr_page--; } } } const default_data = { view_history: view_history, rt: rt, }; 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(); const advance = (rt) => { if (trial.allow_keys) { this.jsPsych.pluginAPI.pressKey(trial.key_forward, rt); } else if (trial.show_clickable_nav) { this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#jspsych-instructions-next"), rt); } }; const backup = (rt) => { if (trial.allow_keys) { this.jsPsych.pluginAPI.pressKey(trial.key_backward, rt); } else if (trial.show_clickable_nav) { this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#jspsych-instructions-back"), rt); } }; let curr_page = 0; let t = 0; for (let i = 0; i < data.view_history.length; i++) { if (i == data.view_history.length - 1) { advance(t + data.view_history[i].viewing_time); } else { if (data.view_history[i + 1].page_index > curr_page) { advance(t + data.view_history[i].viewing_time); } if (data.view_history[i + 1].page_index < curr_page) { backup(t + data.view_history[i].viewing_time); } t += data.view_history[i].viewing_time; curr_page = data.view_history[i + 1].page_index; } } } } InstructionsPlugin.info = info; return InstructionsPlugin; })(jsPsychModule);