Home Reference Source

scripts/experiments/jnd/jnd_timeline.js

import JND from "/scripts/experiments/jnd/jnd.js";
import {get_instructions} from "/scripts/experiment-properties/instructions/instructions_controller.js";

export var jnd_exp = new JND(params);

var timeline = [];
var address = location.protocol + "//" + location.hostname + ":" + location.port; 

// =========================================================
// WELCOME TRIAL BLOCK

var welcome = {
  type: 'html-keyboard-response',
  stimulus: `<div align = "center">` + `<img src="${address}/img/VCL_lab_logo.png"></img><br><br>` +
            `<b>Base:</b> JND` + '<br>' + 
            `<b>Trial Type:</b> ${jnd_exp.trial_structure}` + '<br>' + 
            `<b>Graph Type:</b> ${jnd_exp.graph_type}` + '<br>' + 
            `<b>Condition:</b> ${jnd_exp.condition_name}` + 
            '<br><br><br><p><font size = 15>Press any key to begin.<p></font>' +
            '</div>',
  data: {type: 'instruction'}
};
timeline.push(welcome);

// =========================================================
// INSTRUCTION TRIAL BLOCKS

var ready = {
  type: 'html-keyboard-response',
  stimulus: "<div align = 'center'> <font size = 20><p>Ready? We will first do some practice trials. <p>" + "<br><br><p><b>Press any key to begin.</b></p></font></div>",
  data: {type: 'instruction'}
}

var instructions = {
    type: "html-keyboard-response",
    stimulus: function(){
        return get_instructions(jnd_exp);
    }
};

var instruction_trials = {
  timeline: [instructions, ready]
};

timeline.push(instruction_trials);

// =========================================================
// PRACTICE TRIAL BLOCKS

// ---------------------------------------------------------
// FEEDBACK

var feedback = {
  type: 'html-keyboard-response',
  choices: ['q', 32],
  post_trial_gap: 300,
  // trial_duration: 500,
  data: {type: 'feedback'},
  stimulus: function(){

    document.body.style.backgroundColor = jnd_exp.trial_data.feedback_background_color;

    var last_trial = JSON.parse(jsPsych.data.getLastTrialData().json());
    var last_trial_correct = last_trial[0]["correct"];

    // For debugging purposes:
    if (last_trial_correct == -1){
      return '<p>' + 
             '<font style="font-size:50px; color:blue">Exiting from experiment.<p></font>'
    }

    else if (last_trial_correct){
      return `<div align = "center">
              <p>
                <i class="fa fa-check-circle" style="font-size:50px; color:green; margin-right: 10px;"></i> 
                <font style="font-size:50px; color:green">
                Correct!
                </font>
              <p>
              <br>
              Press spacebar to continue.
              </div>`
    }
    else{
      return `<div align = "center">
              <p>
                <i class="fa fa-close" style="font-size:50px; color:red; margin-right: 10px;"></i>
                <font style="font-size:50px; color:red;"">
                Incorrect!
                </font>
              <p>
              <br>
              Press spacebar to continue.
              </div>`
    }
  }
};

// ---------------------------------------------------------
// PRE-SUBCONDITION INSTRUCTION BLOCK

var practice_jnd = jnd_exp.generate_trial("practice");

// This is a conditional block that will execute only if 
// the subcondition has a subcondition_instructions attribute 
// and it is the start of the subcondition.
var subcond_practice_instruction = {
  type: 'html-keyboard-response',
  stimulus: function(){

    let index = jnd_exp.current_practice_condition_index;
    let constants = jnd_exp.practice_conditions_constants[index];

    return constants.subcondition_instructions;
  }
}

var subcond_practice_instruction_block = {
  timeline: [subcond_practice_instruction],
  conditional_function: function() {

    let index = jnd_exp.current_practice_condition_index;
    let constants = jnd_exp.practice_conditions_constants[index];

    if (constants.subcondition_instructions){
      return true;
    } else {
      return false;
    }
  }
}

// ---------------------------------------------------------
// PRACTICE BLOCK

var practice = {
  timeline: [subcond_practice_instruction_block, practice_jnd, feedback], // We use same feedback block as that used in practice 
  loop_function: function(data){ // Return true if timeline should continue
                                 // Return false if timeline should end

    // Flag is always true for each trial since we display one trial for 
    // each condition on the practice                              
    jnd_exp.first_trial_of_sub_condition = true;

    let key_presses = [];
    data.values().forEach(function(elem){
                          key_presses.push(elem.key_press);
                          });

    // For debugging, if you want to exit out of experiment, press q:
    if (key_presses.includes(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('q'))){
      // Turn flag on 
      jnd_exp.first_trial_of_sub_condition = true;
      return false;
    }

    // If there are still more practice conditions, increment current index
    if (jnd_exp.current_practice_condition_index < (jnd_exp.practice_conditions_constants.length-1)){
      jnd_exp.current_practice_condition_index++; 
      console.log("!!!!!!!!!! Moved to new practice condition at index " 
                  + jnd_exp.current_practice_condition_index);
      return true; 
    }
    // Else end experiment
    else{
      // Turn flag on 
      jnd_exp.first_trial_of_sub_condition = true;
      return false;
    }
  }
};

timeline.push(practice);

// ---------------------------------------------------------
// STOP BLOCK

var stop = {
  type: 'html-keyboard-response',
  stimulus: "<div align = 'center'> <font size = 20><p>This concludes the practice trials.<p>" + "<br><br><p><b>Any questions?</b></p></font></div>",
  data: {type: 'instruction'},
  on_start: function(stop){
    // Reset background color to feedback
    document.body.style.backgroundColor = jnd_exp.trial_data.feedback_background_color;
  }
}

var ready_experiment = {
  type: 'html-keyboard-response',
  stimulus: "<div align = 'center'> <font size = 20><p>Ready?<p>" + "<br><br><p><b>Press any key to begin.</b></p></font></div>",
  data: {type: 'instruction'}
}

var stop_trials = {
  timeline: [stop, ready_experiment]
};

timeline.push(stop_trials);

// =========================================================
// EXPERIMENT TRIAL BLOCKS

// ---------------------------------------------------------
// PRE-SUBCONDITION INSTRUCTION BLOCK

var trial = jnd_exp.generate_trial("test");

// This is a conditional block that will execute only if 
// the subcondition has a subcondition_instructions attribute 
// and it is the start of the subcondition.
var subcond_instruction = {
  type: 'html-keyboard-response',
  stimulus: function(){

    let index = jnd_exp.current_sub_condition_index; 
    let constants = jnd_exp.sub_conditions_constants[index];

    return constants.subcondition_instructions;
  }
}

var subcond_instruction_block = {
  timeline: [subcond_instruction],
  conditional_function: function() {

    let index = jnd_exp.current_sub_condition_index; 
    let constants = jnd_exp.sub_conditions_constants[index];

    if (constants.subcondition_instructions && jnd_exp.is_start_of_subcondition()){
      return true;
    } else {
      return false;
    }
  }
}

// ---------------------------------------------------------
// EXPERIMENT BLOCK

var experiment = {
  timeline: [subcond_instruction_block, trial, feedback],
  loop_function: function(data){ // Return true if timeline should continue
                                 // Return false if timeline should end

    let key_presses = [];
    data.values().forEach(function(elem){
                          key_presses.push(elem.key_press);
                          });

    // For debugging, if you want to exit out of experiment, press q:
    if (key_presses.includes(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('q'))){
      return false;
    }

    // If subcondition should end:
    if(jnd_exp.end_sub_condition()){
      jnd_exp.first_trial_of_sub_condition = true;
      // If there are still more subconditions, increment current index
      if (jnd_exp.current_sub_condition_index < (jnd_exp.sub_conditions_constants.length-1)){
        jnd_exp.current_sub_condition_index++; 
        console.log("!!!!!!!!!! Moved to new sub condition at index " 
                    + jnd_exp.current_sub_condition_index);
        return true; 
      }
      // Else end experiment
      else{
        return false;
      }
    } 
    // Else continue w/ current subcondition:
    else {
      return true;
    }
  },
  on_finish: function(data){
    jnd_exp.trial_data = data; 
  }
};

timeline.push(experiment);

console.log("======================");

// =========================================================
// DATA DOWNLOADING 

var experiment_end = {
  type: 'html-keyboard-response',
  stimulus: '<div align = "center">' + 
            '<p><font size = 10>You have completed the experiment!<p></font>' +
            '<br>' +
            'Trial and summary data files will now automatically download locally.' + 
            '</div>' ,
  on_start: function(){

    jnd_exp.export_trial_data();
    jnd_exp.export_summary_data();
    
    // Reset background color to feedback
    document.body.style.backgroundColor = jnd_exp.trial_data.feedback_background_color;
  }
};
timeline.push(experiment_end);

// =========================================================
// START JSPSYCH

jsPsych.init({
    timeline: timeline,
    on_finish: function(){ 
        jsPsych.data.displayData();
    }
});