aiom
Version:
A Highly Flexible and Modular Framework for Behavioral Experiments
307 lines (272 loc) • 10.8 kB
JavaScript
var local_pid;
var current_on_left;
var start_classes;
var classes;
var class_questions;
var n_rest;
var current_position;
var proposal_position;
var current_category;
var proposal_category;
var current_chain;
var current_class;
var n_trial = 1;
var attention_check_pass = true;
const taskName = window.taskName;
const instruction_text = `/exp-static/${taskName}/instruction.html`;
function show_instruction() {
return $.Deferred(function(deferred) {
$('#insContent p').load(`${instruction_text}`, function(response, status, xhr) {
if (status == "error") {
$("#insContent p").html("Sorry, there was an error loading the instruction.");
}
});
$("#instructions").css("display", "flex");
// Wait for the user to click "Continue"
$("#consentButton").on("click", function() {
$("#instructions").css("display", "none"); // Hide the modal
deferred.resolve(); // Continue the script
});
}).promise();
}
function set_up() {
local_pid = Cookies.get('pid');
axios.post(`/api/${taskName}/set_up`, {
names: local_pid,
}, {headers: {
'Content-Type': 'application/json',
},
})
.then(response => {
classes = response.data.classes;
class_questions = response.data.class_questions;
n_rest = Number(response.data.n_rest);
})
.catch((error) => {
console.error('Error:', error);
alert(`Error in setting tables`);
});
}
function getChoice() {
axios.get(`/api/${taskName}/get_choices`, {
headers: {
'ID': local_pid
},
})
.then(response => {
// parse the information from the response
current_on_left = 0.5 <= Math.random();
if (['likelihood', 'attention_check'].includes(response.data.trial_type)) {
$(".option").removeClass('hidden-option');
current_class = response.data.current_class;
// show question
const question_lh = class_questions[classes.indexOf(current_class)];
const words = question_lh.split(' ');
const lastWord = words.pop(); // Remove and return the last word
const questionStart = words.join(' ');
$(".question").html(`${questionStart}<br><span class="highlight">${lastWord}</span>`);
const left_attrs = { class: 'stimuli_left', height: 128, width: 128 };
const right_attrs = { class: 'stimuli_right', height: 128, width: 128 };
left_attrs['src'] = current_on_left ? response.data.current : response.data.proposal;
right_attrs['src'] = current_on_left ? response.data.proposal : response.data.current;
if (response.data.trial_type === 'attention_check') {
const attention_check = response.data.attention_check;
const is_left_correct = current_on_left ? (current_class === attention_check[0]) : (current_class === attention_check[1]);
const is_right_correct = current_on_left ? (current_class === attention_check[1]) : (current_class === attention_check[0]);
left_attrs.onclick = `sendChoice_attentioncheck(${is_left_correct})`;
right_attrs.onclick = `sendChoice_attentioncheck(${is_right_correct})`;
} else { // 'likelihood' trial
left_attrs.onclick = 'sendChoice(0)';
right_attrs.onclick = 'sendChoice(1)';
current_chain = response.data.current_chain;
current_position = response.data.current_position;
proposal_position = response.data.proposal_position;
}
const $left_img = $('<img>').attr(left_attrs);
const $right_img = $('<img>').attr(right_attrs);
$(".stimuli_left").replaceWith($left_img);
$(".stimuli_right").replaceWith($right_img);
} else {
$(".option").addClass('hidden-option');
current_chain = response.data.current_chain;
current_position = response.data.stimulus_position;
current_category = response.data.current;
proposal_category = response.data.proposal;
const left_button_text = current_on_left ? current_category : proposal_category;
const right_button_text = current_on_left ? proposal_category : current_category;
$(".stimuli_left").replaceWith(
`<button class="stimuli_left" id="button_left" onclick="sendChoice_prior(0)">${left_button_text}</button>`);
$(".stimuli_right").replaceWith(
`<button class="stimuli_right" id="button_right" onclick="sendChoice_prior(1)">${right_button_text}</button>`);
const question_img_attrs = { id: 'question_stimuli', height: 128, width: 128 };
question_img_attrs['src'] = response.data.current_stimulus;
const $question_img = $('<img>').attr(question_img_attrs);
$(".question").html('Which option can best describe the image:<br><br>').append($question_img);
}
fadein_option();
return response.data;
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
errorhandler();
});
}
function sendChoice_attentioncheck(attention_check) {
$(".stimuli_left, .stimuli_right").removeAttr("onclick");
axios.post(`/api/register_attentioncheck`, {
result: attention_check,
},
{headers: {
'Content-Type': 'application/json',
'ID': local_pid,
},
})
.then(response => {
if (response.data.fail_count >= 2) {
attention_check_pass = false;
}
fadeaway_option(100);
setTimeout(() => {
getChoice();
}, 500)
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
errorhandler();
});
}
function sendChoice(selected) {
$(".stimuli_left, .stimuli_right").removeAttr("onclick");
let decision;
if (current_on_left) {
decision = selected;
} else {
decision = 1-selected;
}
const selected_position = decision==0 ? current_position : proposal_position;
axios.post(`/api/${taskName}/register_choices`, {
choice: selected_position,
},
{headers: {
'Content-Type': 'application/json',
'name': local_pid,
'ID': `${local_pid}_blockwise_no${current_chain}`,
'n_trial': n_trial,
'current_class': current_class,
'trial_type': 'likelihood',
},
})
.then(response => {
n_trial ++;
if (!response.data.finish) {
if ((n_trial-1)%n_rest===0 && n_trial != 2) {
time_to_rest().then(() => {
// Code here will run after the user clicks "Continue"
fadeaway_option(response.data.progress);
setTimeout(() => {
getChoice();
}, 500)
});
} else {
fadeaway_option(response.data.progress);
setTimeout(() => {
getChoice();
}, 500)
}
} else {
endExperiment();
}
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
errorhandler();
});
}
function sendChoice_prior(selected) {
$(".stimuli_left, .stimuli_right").removeAttr("onclick");
let decision;
if (current_on_left) {
decision = selected;
} else {
decision = 1-selected;
}
const selected_category = decision==0 ? current_category : proposal_category;
axios.post(`/api/${taskName}/register_choices`, {
choice: selected_category,
current_position: current_position,
},
{headers: {
'Content-Type': 'application/json',
'ID': `${local_pid}_blockwise_no${current_chain}`,
'name': local_pid,
'n_trial': n_trial,
'trial_type': 'prior',
},
})
.then(response => {
n_trial ++;
if (!response.data.finish) {
if ((n_trial-1)%n_rest===0 && n_trial != 2) {
time_to_rest().then(() => {
// Code here will run after the user clicks "Continue"
fadeaway_option(response.data.progress);
setTimeout(() => {
getChoice();
}, 500)
});
} else {
fadeaway_option(response.data.progress);
setTimeout(() => {
getChoice();
}, 500)
}
} else {
endExperiment();
}
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
errorhandler();
});
}
function endExperiment() {
if (attention_check_pass) window.location.href = `/experiment/${taskName}`; // continue to the next subexperiment
else window.location.href = '/early_stop'; // end the experiment without bonus
}
function errorhandler() {
window.location.href = '/error';
}
// UI animation
function fadeaway_option(progress) {
$('#choice_left').removeClass('fade-in').addClass('fade-out');
$('#choice_right').removeClass('fade-in').addClass('fade-out');
setTimeout(() => {
updateProgress(progress);
}, 100);
}
function fadein_option() {
$('#choice_left').removeClass('fade-out').addClass('fade-in');
$('#choice_right').removeClass('fade-out').addClass('fade-in');
}
function updateProgress(progress) {
if (progress <= 1.0) {
const progressBar = document.getElementById('progressBar');
progressBar.style.width = `${progress*100}%`;
}
}
function time_to_rest() {
return $.Deferred(function(deferred) {
// Display the modal
$('#restContent p').html('You can have a rest now!');
$("#rest").css("display", "flex");
// Wait for the user to click "Continue"
$("#continueButton").on("click", function() {
$("#rest").css("display", "none"); // Hide the modal
deferred.resolve(); // Continue the script
});
}).promise();
}