var jsPsychPreload = (function (jspsych) { 'use strict'; const info = { name: "preload", parameters: { /** Whether or not to automatically preload any media files based on the timeline passed to jsPsych.run. */ auto_preload: { type: jspsych.ParameterType.BOOL, pretty_name: "Auto-preload", default: false, }, /** A timeline of trials to automatically preload. If one or more trial objects is provided in the timeline array, then the plugin will attempt to preload the media files used in the trial(s). */ trials: { type: jspsych.ParameterType.TIMELINE, pretty_name: "Trials", default: [], }, /** * Array with one or more image files to load. This parameter is often used in cases where media files cannot# * be automatically preloaded based on the timeline, e.g. because the media files are passed into an image plugin/parameter with * timeline variables or dynamic parameters, or because the image is embedded in an HTML string. */ images: { type: jspsych.ParameterType.STRING, pretty_name: "Images", default: [], array: true, }, /** * Array with one or more audio files to load. This parameter is often used in cases where media files cannot * be automatically preloaded based on the timeline, e.g. because the media files are passed into an audio plugin/parameter with * timeline variables or dynamic parameters, or because the audio is embedded in an HTML string. */ audio: { type: jspsych.ParameterType.STRING, pretty_name: "Audio", default: [], array: true, }, /** * Array with one or more video files to load. This parameter is often used in cases where media files cannot * be automatically preloaded based on the timeline, e.g. because the media files are passed into a video plugin/parameter with * timeline variables or dynamic parameters, or because the video is embedded in an HTML string. */ video: { type: jspsych.ParameterType.STRING, pretty_name: "Video", default: [], array: true, }, /** HTML-formatted message to be shown above the progress bar while the files are loading. */ message: { type: jspsych.ParameterType.HTML_STRING, pretty_name: "Message", default: null, }, /** Whether or not to show the loading progress bar. */ show_progress_bar: { type: jspsych.ParameterType.BOOL, pretty_name: "Show progress bar", default: true, }, /** * Whether or not to continue with the experiment if a loading error occurs. If false, then if a loading error occurs, * the error_message will be shown on the page and the trial will not end. If true, then if if a loading error occurs, the trial will end * and preloading failure will be logged in the trial data. */ continue_after_error: { type: jspsych.ParameterType.BOOL, pretty_name: "Continue after error", default: false, }, /** Error message to show on the page in case of any loading errors. This parameter is only relevant when continue_after_error is false. */ error_message: { type: jspsych.ParameterType.HTML_STRING, pretty_name: "Error message", default: "The experiment failed to load.", }, /** * Whether or not to show a detailed error message on the page. If true, then detailed error messages will be shown on the * page for all files that failed to load, along with the general error_message. This parameter is only relevant when continue_after_error is false. */ show_detailed_errors: { type: jspsych.ParameterType.BOOL, pretty_name: "Show detailed errors", default: false, }, /** * The maximum amount of time that the plugin should wait before stopping the preload and either ending the trial * (if continue_after_error is true) or stopping the experiment with an error message (if continue_after_error is false). * If null, the plugin will wait indefintely for the files to load. */ max_load_time: { type: jspsych.ParameterType.INT, pretty_name: "Max load time", default: null, }, /** Function to be called after a file fails to load. The function takes the file name as its only argument. */ on_error: { type: jspsych.ParameterType.FUNCTION, pretty_name: "On error", default: null, }, /** Function to be called after a file loads successfully. The function takes the file name as its only argument. */ on_success: { type: jspsych.ParameterType.FUNCTION, pretty_name: "On success", default: null, }, }, }; /** * **preload** * * jsPsych plugin for preloading image, audio, and video files * * @author Becky Gilbert * @see {@link https://www.jspsych.org/plugins/jspsych-preload/ preload plugin documentation on jspsych.org} */ class PreloadPlugin { constructor(jsPsych) { this.jsPsych = jsPsych; } trial(display_element, trial) { var success = null; var timeout = false; var failed_images = []; var failed_audio = []; var failed_video = []; var detailed_errors = []; var in_safe_mode = this.jsPsych.getSafeModeStatus(); // create list of media to preload // var images = []; var audio = []; var video = []; if (trial.auto_preload) { var experiment_timeline = this.jsPsych.getTimeline(); var auto_preload = this.jsPsych.pluginAPI.getAutoPreloadList(experiment_timeline); images = images.concat(auto_preload.images); audio = audio.concat(auto_preload.audio); video = video.concat(auto_preload.video); } if (trial.trials.length > 0) { var trial_preloads = this.jsPsych.pluginAPI.getAutoPreloadList(trial.trials); images = images.concat(trial_preloads.images); audio = audio.concat(trial_preloads.audio); video = video.concat(trial_preloads.video); } images = images.concat(trial.images); audio = audio.concat(trial.audio); video = video.concat(trial.video); images = this.jsPsych.utils.unique(images.flat()); audio = this.jsPsych.utils.unique(audio.flat()); video = this.jsPsych.utils.unique(video.flat()); if (in_safe_mode) { // don't preload video if in safe mode (experiment is running via file protocol) video = []; } // render display of message and progress bar var html = ""; if (trial.message !== null) { html += trial.message; } if (trial.show_progress_bar) { html += `
Loading timed out.
" +
"Consider compressing your stimuli files, loading your files in smaller batches,
" +
"and/or increasing the max_load_time parameter.
Error details:
"; detailed_errors.forEach((e) => { display_element.innerHTML += e; }); } }; const end_trial = () => { // clear timeout again when end_trial is called, to handle race condition with max_load_time this.jsPsych.pluginAPI.clearAllTimeouts(); var trial_data = { success: success, timeout: timeout, failed_images: failed_images, failed_audio: failed_audio, failed_video: failed_video, }; // clear the display display_element.innerHTML = ""; this.jsPsych.finishTrial(trial_data); }; // do preloading if (trial.max_load_time !== null) { this.jsPsych.pluginAPI.setTimeout(on_timeout, trial.max_load_time); } var total_n = images.length + audio.length + video.length; var loaded = 0; // success or error count var loaded_success = 0; // success count if (total_n == 0) { on_success(); } else { const load_video = (cb) => { this.jsPsych.pluginAPI.preloadVideo(video, cb, file_loading_success, file_loading_error); }; const load_audio = (cb) => { this.jsPsych.pluginAPI.preloadAudio(audio, cb, file_loading_success, file_loading_error); }; const load_images = (cb) => { this.jsPsych.pluginAPI.preloadImages(images, cb, file_loading_success, file_loading_error); }; if (video.length > 0) { load_video(() => { }); } if (audio.length > 0) { load_audio(() => { }); } if (images.length > 0) { load_images(() => { }); } } // helper functions and callbacks // called when a single file loading fails function file_loading_error(e) { // update progress bar even if there's an error update_loading_progress_bar(); // change success flag after first file loading error if (success == null) { success = false; } // add file to failed media list var source = "unknown file"; if (e.source) { source = e.source; } if (e.error && e.error.path && e.error.path.length > 0) { if (e.error.path[0].localName == "img") { failed_images.push(source); } else if (e.error.path[0].localName == "audio") { failed_audio.push(source); } else if (e.error.path[0].localName == "video") { failed_video.push(source); } } // construct detailed error message var err_msg = "Error loading file: " + source + "
";
if (e.error.statusText) {
err_msg += "File request response status: " + e.error.statusText + "
";
}
if (e.error == "404") {
err_msg += "404 - file not found.
";
}
if (typeof e.error.loaded !== "undefined" &&
e.error.loaded !== null &&
e.error.loaded !== 0) {
err_msg += e.error.loaded + " bytes transferred.";
}
else {
err_msg +=
"File did not begin loading. Check that file path is correct and reachable by the browser,
" +
"and that loading is not blocked by cross-origin resource sharing (CORS) errors.";
}
err_msg += "