aiom_pack
Version:
Framework for interdependent (mcmc-like) behavioral experiments
311 lines (279 loc) • 10.7 kB
JavaScript
var local_pid;
var current_on_left;
var start_classes;
var classes;
var class_questions;
var n_rest;
var mode;
var stimuli_attr;
var current_position;
var proposal_position;
var current_category;
var proposal_category;
var current_chain;
var current_class;
var n_trial = 1;
var n_turnpoint = 0;
var attention_check_pass = true;
function submit_id(id) {
Cookies.set('pid', id);
axios.post('/api/set_table', {
names: id,
}, {headers: {
'Content-Type': 'application/json',
},
})
.then(response => {
classes = JSON.stringify(response.data.classes);
class_questions = JSON.stringify(response.data.class_questions);
Cookies.set('classes', classes);
Cookies.set('class_questions', class_questions);
Cookies.set('n_rest', response.data.n_rest);
Cookies.set('mode', response.data.mode);
})
.then(() => {window.location.href = `/instruction`;})
.catch((error) => {
console.error('Error:', error);
alert(`Error in setting tables`);
});
}
function load_parameters() {
local_pid = Cookies.get('pid');
classes = Cookies.get('classes');
classes = JSON.parse(classes);
class_questions = Cookies.get('class_questions');
class_questions = JSON.parse(class_questions);
n_rest = Number(Cookies.get('n_rest'));
mode = Cookies.get('mode');
if (mode === 'test') {
stimuli_attr = 'alt';
} else if (mode === 'image') {
stimuli_attr = 'src';
}
}
function getChoice() {
axios.get(`/api/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;
$( ".stimuli_left" ).replaceWith('<img class="stimuli_left" src="" alt="" height="128" width="128" onclick="sendChoice(0)">');
$( ".stimuli_right" ).replaceWith('<img class="stimuli_right" src="" alt="" height="128" width="128" onclick="sendChoice(1)">');
$(".question").html(class_questions[classes.indexOf(current_class)]);
if (current_on_left) {
$(".stimuli_left").attr(stimuli_attr, response.data.current);
$(".stimuli_right").attr(stimuli_attr, response.data.proposal);
} else {
$(".stimuli_right").attr(stimuli_attr, response.data.current);
$(".stimuli_left").attr(stimuli_attr, response.data.proposal);
}
if (response.data.trial_type === 'attention_check') {
const attention_check = response.data.attention_check;
// mute the onclick function of two stimuli
$(".stimuli_left, .stimuli_right").attr("onclick", "");
if (current_on_left) {
$(".stimuli_left").attr("onclick", `sendChoice_attentioncheck(${current_class===attention_check[0]})`);
$(".stimuli_right").attr("onclick", `sendChoice_attentioncheck(${current_class===attention_check[1]})`);
} else {
$(".stimuli_left").attr("onclick", `sendChoice_attentioncheck(${current_class===attention_check[1]})`);
$(".stimuli_right").attr("onclick", `sendChoice_attentioncheck(${current_class===attention_check[0]})`);
}
} else {
current_chain = response.data.current_chain;
current_position = response.data.current_position;
proposal_position = response.data.proposal_position;
}
} 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;
$( ".stimuli_left" ).replaceWith(
'<button class="stimuli_left" id="button_left" onclick="sendChoice_prior(0)">Option</button>'
);
$( ".stimuli_right" ).replaceWith(
'<button class="stimuli_right" id="button_right" onclick="sendChoice_prior(1)">Option</button>'
);
$(".question").html('Which category can best describe the face:<br><br>');
$(".question").append('<img id="question_stimuli" src="" alt="" height="128" width="128"></img>');
$("#question_stimuli").attr(stimuli_attr, response.data.current_stimulus);
if (current_on_left) {
$(".stimuli_left").html(current_category);
$(".stimuli_right").html(proposal_category);
} else {
$(".stimuli_right").html(current_category);
$(".stimuli_left").html(proposal_category);
}
}
fadein_option();
return response.data;
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
endExperiment();
});
}
function sendChoice_attentioncheck(attention_check) {
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);
// remove the click function of two stimuli
$(".stimuli_left, .stimuli_right").attr("onclick", "");
$(".stimuli_left").attr("onclick", "sendChoice(0)");
$(".stimuli_right").attr("onclick", "sendChoice(1)");
setTimeout(() => {
getChoice();
}, 500)
})
.catch((error) => {
console.error('Error:', error);
// alert(`Error sending list ${local_pid}`);
endExperiment();
});
}
function sendChoice(selected) {
let decision;
if (current_on_left) {
decision = selected;
} else {
decision = 1-selected;
}
const selected_position = decision==0 ? current_position : proposal_position;
axios.post(`/api/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}`);
endExperiment();
});
}
function sendChoice_prior(selected) {
let decision;
if (current_on_left) {
decision = selected;
} else {
decision = 1-selected;
}
const selected_category = decision==0 ? current_category : proposal_category;
axios.post(`/api/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}`);
endExperiment();
});
}
function endExperiment() {
if (attention_check_pass) window.location.href = `/thanks`; // continue to the next subexperiment
else window.location.href = `/early_stop`; // end the experiment without bonus
}
// 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();
}