Home Reference Source

scripts/experiment-properties/instructions/instructions_controller.js

export {get_instructions}

/**
 * Generates the instructions html for the given experiment.
 *
 * @param   {object}   experiment
 *
 * @return  {string}   html for the instructions jsPsych block
 */
function get_instructions(experiment) {

  // EXPERIMENTS from experiments-config.js
  let experiment_name = experiment.constructor.name.toLowerCase();
  let instructions_info = EXPERIMENTS[experiment_name].instructions;

  if (!instructions_info.default_html || instructions_info.default_html.length === 0) {
    throw new Error("No default html specified for experiment " + experiment_name);
  }

  if (!instructions_info.default_images || instructions_info.default_images.length === 0) {
    throw new Error("No default images are defined for experiment " + experiment_name);
  }

  let default_html = instructions_info.default_html;
  let default_images = instructions_info.default_images;

  let condition_instructions_object = CONDITIONS[experiment.condition_name].instructions;

  // If there is custom html/images defined for this experiment
  if (condition_instructions_object) {
    return get_custom_instructions(experiment, condition_instructions_object, default_html, default_images);
  }
  // Else use default html and images
  else {
    return get_html_with_images(default_html, default_images);  
  }

}

/**
 * Generates custom instructions depending on whether custom_html is defined, or 
 * custom_images is defined.
 *
 * @param   {object}   experiment
 * @param   {object}   condition_instructions_object
 * @param   {string}   default_html
 * @param   {array}    default_images
 *
 * @return  {string}   html for custom instructions
 */
function get_custom_instructions(experiment, condition_instructions_object, default_html, default_images) {

  // Validation check that experiment in instructions object is a supported experiment for the condition
  Object.keys(condition_instructions_object).forEach(function(exp) {
    if (!CONDITIONS[experiment.condition_name]["experiment"].includes(exp)) {
      throw new Error("Experiment " + exp + " is listed within instructions key but is not among the supported experiments " + 
                      "listed for the condition. Listed experiments are: " + CONDITIONS[experiment.condition_name]["experiment"] + ".");
    }
  });

  let condition_instructions = condition_instructions_object[experiment.constructor.name.toLowerCase()];
  
  // If no instructions for this experiment, use default
  if (!condition_instructions) {
    return get_html_with_images(default_html, default_images);
  }
  else {

    // Validation check that must only have one key (custom_html or custom_images) defined
    if (Object.keys(condition_instructions).length > 1) {
      throw new Error("Only one key permitted inside instructions object for experiment " + experiment.constructor.name.toLowerCase() + ". Must be " + 
                      "either custom_html or custom_images.");
    }

    // If there is custom html
    if (condition_instructions.custom_html) {
      return condition_instructions.custom_html;
    }
    // If there is custom images
    else if (condition_instructions.custom_images) {
      let images = condition_instructions.custom_images;

      // Validation check that specified num of custom_images == num of default_images
      if (images.length !== default_images.length) {
        throw new Error("The default instruction for this experiment requires " + default_images.length + " image(s). " + images.length + " image(s) were specified for the condition instead.");
      }

      return get_html_with_images(default_html, images);
    } else {
      throw new Error("No custom_html or custom_images defined in instructions object for this condition for experiment " + experiment.constructor.name);
    }

  }
}

/**
 * Replaces any 'image1.png', 'image2.png' ... etc with each string 
 * entry in images.
 *
 * NOTE: When defining default_html in experiments-config.js, need to name
 *       images as 'image1.png', 'image2.png' so this code knows what to replace
 *
 * @param   {string}   html
 * @param   {array}    images 
 *
 * @return  {string}   html with any 'image1.png' etc replaced with entries in images
 */
function get_html_with_images(html, images) {

  let counter = 1;

  for (let image_url of images) {
    let url_to_replace = "image" + counter + ".png";

    html = html.replace(url_to_replace, image_url);

    counter++;
  }

  return html;
}