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();
}
});