605 lines
16 KiB
JavaScript
605 lines
16 KiB
JavaScript
import { initJsPsych } from 'jspsych';
|
|
import 'jspsych/css/jspsych.css';
|
|
import jsPsychHtmlKeyboardResponse from '@jspsych/plugin-html-keyboard-response';
|
|
import jsPsychFullscreen from '@jspsych/plugin-fullscreen';
|
|
import jsPsychHtmlButtonResponse from '@jspsych/plugin-html-button-response';
|
|
import jsPsychSurvey from '@jspsych/plugin-survey';
|
|
import '@jspsych/plugin-survey/css/survey.css';
|
|
import './styles.css';
|
|
import { getStimulusMap } from './scripts/text-stimuli.js';
|
|
import { textStimuli } from './scripts/text-stimuli.js';
|
|
import JsPsychDieRoll from './plugins/jspsych-die-roll.js';
|
|
import JsPsychCaptcha from './plugins/jspsych-captcha.js';
|
|
import html from './utils/html.js';
|
|
|
|
const experiment_name = import.meta.env.VITE_EXPERIMENT_NAME;
|
|
|
|
let prolific_id;
|
|
let probe_condition; // will be set to ai or human based on the condition.
|
|
let debug;
|
|
let probe_order; // will be set to ai_first or human_first based on the condition
|
|
const CAPTCHA_TRIAL_COUNT = debug ? 2 : 12; // set to 10 for main experiment
|
|
|
|
function delayed_redirect(url) {
|
|
setTimeout(() => {
|
|
window.location = url;
|
|
}, 5000);
|
|
}
|
|
|
|
const jsPsych = initJsPsych({
|
|
on_finish: function () {
|
|
jsPsych.getDisplayElement().innerHTML = textStimuli.complete;
|
|
},
|
|
on_close: function () {
|
|
delayed_redirect(import.meta.env.VITE_CLOSED_URL);
|
|
},
|
|
on_data_update: function () {
|
|
if (debug) {
|
|
console.log(jsPsych.data.get().json());
|
|
}
|
|
},
|
|
show_progress_bar: true,
|
|
});
|
|
|
|
debug =
|
|
jsPsych.data.getURLVariable('debug') === 'true' ||
|
|
import.meta.env.VITE_DEBUG === 'true';
|
|
|
|
|
|
prolific_id = jsPsych.data.getURLVariable('PROLIFIC_PID');
|
|
|
|
const COND = Number(jsPsych.data.getURLVariable('C'));
|
|
|
|
const probe_closing_text = `<p class ="mt-6">On the next page, you will be asked about your suspicions and thoughts about this aspect of the study.</p>
|
|
<p class="mt-6">Press <span class = "font-semibold"> SPACE</span> to continue</p>`;
|
|
|
|
const probe_text_die =
|
|
`<p> In this study, the die roll to determine captcha difficulty was rigged: the number you rolled was predetermined.</p><p class="mt-2"> Therefore, the difficulty of the captcha task was also predetermined.</p>` +
|
|
probe_closing_text;
|
|
const probe_text_difficulty =
|
|
`In this study, the die roll to determine captcha difficulty was indeed random. </p><p class="mt-2"> However, the difficulty of the captcha task was pre-determined. It was unrelated to the die roll.
|
|
</p>` +
|
|
probe_closing_text;
|
|
|
|
const probe_text_baseline = probe_closing_text
|
|
|
|
const die_result = Math.random() < 0.5 ? 3 : 4;
|
|
|
|
switch (COND) {
|
|
case 0:
|
|
probe_condition = 'die';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 1:
|
|
probe_condition = 'die';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 2:
|
|
probe_condition = 'die';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
case 3:
|
|
probe_condition = 'die';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
case 4:
|
|
probe_condition = 'difficulty';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 5:
|
|
probe_condition = 'difficulty';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 6:
|
|
probe_condition = 'difficulty';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
case 7:
|
|
probe_condition = 'difficulty';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
case 8:
|
|
probe_condition = 'baseline';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 9:
|
|
probe_condition = 'baseline';
|
|
probe_order = 'die_first';
|
|
break;
|
|
case 10:
|
|
probe_condition = 'baseline';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
case 11:
|
|
probe_condition = 'baseline';
|
|
probe_order = 'difficulty_first';
|
|
break;
|
|
}
|
|
|
|
const stimulusMap = getStimulusMap();
|
|
|
|
const props = {
|
|
condition: probe_condition,
|
|
prolific_id: prolific_id,
|
|
experiment_name: experiment_name,
|
|
probe_order: probe_order,
|
|
cond: COND,
|
|
};
|
|
|
|
if (debug) {
|
|
console.log(props);
|
|
}
|
|
|
|
jsPsych.data.addProperties(props);
|
|
|
|
const timeline = [];
|
|
|
|
const pre_consent_info = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('pre_consent_info'),
|
|
};
|
|
|
|
const enter_fullscreen = {
|
|
type: jsPsychFullscreen,
|
|
fullscreen_mode: true,
|
|
};
|
|
|
|
const consent_form = {
|
|
type: jsPsychHtmlButtonResponse,
|
|
stimulus: stimulusMap.get('consent'),
|
|
choices: ['Exit', 'Continue'],
|
|
on_finish: function (data) {
|
|
if (data.response === 0) {
|
|
jsPsych.abortExperiment(stimulusMap.get('no_consent'));
|
|
}
|
|
},
|
|
};
|
|
|
|
const instructions_1 = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('instructions_1'),
|
|
};
|
|
|
|
const instructions_2 = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('instructions_2'),
|
|
};
|
|
|
|
const instructions_3 = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('instructions_3'),
|
|
};
|
|
|
|
const debrief = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('debrief'),
|
|
};
|
|
|
|
const pre_survey_info = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: stimulusMap.get('pre_survey_info'),
|
|
};
|
|
|
|
const die_roll_practice_trial = {
|
|
type: JsPsychDieRoll,
|
|
prompt: html`
|
|
<p class="mt-2">Before the real die roll, you can practice here.</p>
|
|
<p class="mt-2">
|
|
Click the die once to start it rolling, then click it again to stop.
|
|
</p>
|
|
`,
|
|
practice_trial: true,
|
|
practice_roll_limit: 3,
|
|
roll_duration: 2200,
|
|
result_template:
|
|
'Practice roll {{roll_number}}/{{roll_limit}}: You rolled a {{value}}.',
|
|
};
|
|
|
|
const die_roll_trial = {
|
|
type: JsPsychDieRoll,
|
|
prompt: html`
|
|
<p class="mt-2">
|
|
Click the die once to start it rolling, then click it again to stop.
|
|
</p>
|
|
`,
|
|
rigged_value: die_result,
|
|
roll_duration: 2200,
|
|
result_template: `You rolled a {{value}} (moderate difficulty). Click 'continue' to proceed.`,
|
|
};
|
|
|
|
const create_captcha_trial = trial_index => {
|
|
return {
|
|
type: JsPsychCaptcha,
|
|
prompt:
|
|
'<p class="leading-relaxed text-center">Enter the characters you see into the textbox below.</p>',
|
|
difficulty: die_result,
|
|
button_label: 'Continue',
|
|
error_text: '',
|
|
allow_refresh: false,
|
|
require_correct: false,
|
|
data: {
|
|
trial_id: 'captcha_entry',
|
|
captcha_index: trial_index + 1,
|
|
captcha_total: CAPTCHA_TRIAL_COUNT,
|
|
},
|
|
};
|
|
};
|
|
|
|
const pre_die_roll_info = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: `<p>Practice complete. Now you will roll a die to determine the difficulty of the captcha task.</p>
|
|
<p class="mt-2">Press <span class ="font-bold">SPACE</span> to continue</p>`,
|
|
};
|
|
|
|
const captcha_trials = Array.from({ length: CAPTCHA_TRIAL_COUNT }, (_, index) =>
|
|
create_captcha_trial(index)
|
|
);
|
|
|
|
const survey_function = survey => {
|
|
survey.onAfterRenderPage.add(function (sender, options) {
|
|
console.log('Survey page rendered:', sender.currentPage);
|
|
if (survey.activePage.name === 'page2' || survey.activePage.name === 'page1') {
|
|
const nextButton = document.querySelector('#sv-nav-next > div > input');
|
|
if (nextButton) {
|
|
let seconds = 20;
|
|
const originalText =
|
|
nextButton.value.replace(/\s*\(\d+\)$/, '') || 'Continue';
|
|
nextButton.disabled = true;
|
|
nextButton.value = `${originalText} (${seconds})`;
|
|
const interval = setInterval(() => {
|
|
seconds--;
|
|
nextButton.value = `${originalText} (${seconds})`;
|
|
if (seconds <= 0) {
|
|
clearInterval(interval);
|
|
nextButton.disabled = false;
|
|
nextButton.value = originalText;
|
|
}
|
|
}, 1000);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const survey_1 = {
|
|
type: jsPsychSurvey,
|
|
survey_function: survey_function,
|
|
survey_json: {
|
|
showQuestionNumbers: false,
|
|
completeText: 'Continue',
|
|
pageNextText: 'Continue',
|
|
pagePrevText: 'Previous',
|
|
showPrevButton: false,
|
|
pages: [
|
|
{
|
|
name: 'page1',
|
|
elements: [
|
|
{
|
|
type: 'matrix',
|
|
name:
|
|
'Please answer the following questions about your experience in the captcha task.',
|
|
alternateRows: true,
|
|
isAllRowRequired: debug ? false : true,
|
|
rowOrder: 'random',
|
|
rows: [
|
|
{
|
|
text: `I felt lucky during the die roll.`,
|
|
value: 'Luck',
|
|
},
|
|
{
|
|
text: `I felt unlucky during the die roll.`,
|
|
value: 'Bad_luck',
|
|
},
|
|
],
|
|
columns: [
|
|
{
|
|
value: 5,
|
|
text: 'Strongly agree',
|
|
},
|
|
{
|
|
value: 4,
|
|
text: 'Agree',
|
|
},
|
|
{
|
|
value: 3,
|
|
text: 'Neutral',
|
|
},
|
|
{
|
|
value: 2,
|
|
text: 'Disagree',
|
|
},
|
|
{
|
|
value: 1,
|
|
text: 'Strongly disagree',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'page2',
|
|
elements: [
|
|
{
|
|
type: 'matrix',
|
|
name:
|
|
'Please answer the following questions about your experience in the captcha task.',
|
|
alternateRows: true,
|
|
isAllRowRequired: debug ? false : true,
|
|
rowOrder: 'random',
|
|
rows: [
|
|
{
|
|
text: `I found the captcha task difficult.`,
|
|
value: 'Difficulty',
|
|
},
|
|
{
|
|
text: `I think I solved all the captchas correctly.`,
|
|
value: 'Accuracy',
|
|
},
|
|
{
|
|
text: `I think I solved captchas faster than most other participants would have.`,
|
|
value: 'Relative_performance',
|
|
},
|
|
{
|
|
text: `I think I solved captchas slower than most other participants would have.`,
|
|
value: 'Relative_performance_slow',
|
|
},
|
|
],
|
|
columns: [
|
|
{
|
|
value: 5,
|
|
text: 'Strongly agree',
|
|
},
|
|
{
|
|
value: 4,
|
|
text: 'Agree',
|
|
},
|
|
{
|
|
value: 3,
|
|
text: 'Neutral',
|
|
},
|
|
{
|
|
value: 2,
|
|
text: 'Disagree',
|
|
},
|
|
{
|
|
value: 1,
|
|
text: 'Strongly disagree',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const pre_manip_info = {
|
|
type: jsPsychHtmlKeyboardResponse,
|
|
choices: [' '],
|
|
stimulus: function () {
|
|
switch (probe_condition) {
|
|
case 'die':
|
|
return probe_text_die;
|
|
case 'difficulty':
|
|
return probe_text_difficulty;
|
|
case 'baseline':
|
|
return probe_text_baseline;
|
|
default:
|
|
return 'ERROR: probe condition not recognized';
|
|
}
|
|
},
|
|
};
|
|
|
|
|
|
const die_probe_rows = [{
|
|
text:
|
|
'I suspected that the die roll that determined captcha difficulty was rigged',
|
|
value: 'SuspicionDie1',
|
|
}, {
|
|
text:
|
|
'I suspected that the number I rolled on the die roll was predetermined',
|
|
value: 'SuspicionDie2',
|
|
}];
|
|
|
|
const difficulty_probe_rows = [{
|
|
text:
|
|
'I suspected that the captcha difficulty was unrelated to the die roll',
|
|
value: 'SuspicionDifficulty1',
|
|
}, {
|
|
text:
|
|
'I suspected that the number I rolled did not determine the captcha difficulty',
|
|
value: 'SuspicionDifficulty2',
|
|
}];
|
|
|
|
//shuffle the probe rows:
|
|
|
|
die_probe_rows.sort(() => Math.random() - 0.5);
|
|
difficulty_probe_rows.sort(() => Math.random() - 0.5);
|
|
|
|
let page_1_probe
|
|
let page_2_probe
|
|
|
|
const catch_row = {
|
|
text: 'Attention check: please select "Disagree for this statement.',
|
|
value: 'AttentionCheck',
|
|
};
|
|
|
|
if (Math.random() < 0.5) {
|
|
die_probe_rows.splice(1, 0, catch_row);
|
|
} else {
|
|
difficulty_probe_rows.splice(1, 0, catch_row);
|
|
}
|
|
|
|
if (probe_order === 'die_first') {
|
|
page_1_probe = die_probe_rows
|
|
page_2_probe = difficulty_probe_rows
|
|
} else {
|
|
page_1_probe = difficulty_probe_rows
|
|
page_2_probe = die_probe_rows
|
|
}
|
|
|
|
const survey_2 = {
|
|
type: jsPsychSurvey,
|
|
survey_function: survey_function,
|
|
survey_json: {
|
|
showQuestionNumbers: false,
|
|
completeText: 'Done!',
|
|
pageNextText: 'Continue',
|
|
pagePrevText: 'Previous',
|
|
showPrevButton: false,
|
|
pages: [
|
|
{
|
|
name: 'page1',
|
|
elements: [
|
|
{
|
|
type: 'matrix',
|
|
name: "Please indicate your agreement with the following statements.",
|
|
alternateRows: true,
|
|
isAllRowRequired: debug ? false : true,
|
|
rowOrder: 'random',
|
|
rows: page_1_probe,
|
|
columns: [
|
|
{
|
|
value: 5,
|
|
text: 'Strongly agree',
|
|
},
|
|
{
|
|
value: 4,
|
|
text: 'Agree',
|
|
},
|
|
{
|
|
value: 3,
|
|
text: 'Neutral',
|
|
},
|
|
{
|
|
value: 2,
|
|
text: 'Disagree',
|
|
},
|
|
{
|
|
value: 1,
|
|
text: 'Strongly disagree',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'page2',
|
|
elements: [
|
|
{
|
|
type: 'matrix',
|
|
name: "Please indicate your agreement with the following statements.",
|
|
alternateRows: true,
|
|
isAllRowRequired: debug ? false : true,
|
|
rowOrder: 'random',
|
|
rows: page_2_probe,
|
|
columns: [
|
|
{
|
|
value: 5,
|
|
text: 'Strongly agree',
|
|
},
|
|
{
|
|
value: 4,
|
|
text: 'Agree',
|
|
},
|
|
{
|
|
value: 3,
|
|
text: 'Neutral',
|
|
},
|
|
{
|
|
value: 2,
|
|
text: 'Disagree',
|
|
},
|
|
{
|
|
value: 1,
|
|
text: 'Strongly disagree',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'page3',
|
|
elements: [
|
|
{
|
|
type: 'comment',
|
|
title: `Please write a sentence or two on what you thought the study was about.`,
|
|
isRequired: debug == true ? false : true,
|
|
},
|
|
{
|
|
type: 'comment',
|
|
title: ` Indicate any other thoughts or suspicions you had about the study. \n`,
|
|
isRequired: debug == true ? false : true,
|
|
}
|
|
],
|
|
},
|
|
{
|
|
name: 'page4',
|
|
elements: [
|
|
{
|
|
type: 'radiogroup',
|
|
title: 'Please indicate your gender',
|
|
choices: ['Male', 'Female', 'Other'],
|
|
isRequired: debug ? false : true,
|
|
colCount: 0,
|
|
name: 'gender',
|
|
},
|
|
{
|
|
type: 'radiogroup',
|
|
title: 'Please indicate your handedness',
|
|
choices: ['Right', 'Left', 'Ambidextrous/Other'],
|
|
isRequired: debug ? false : true,
|
|
colCount: 0,
|
|
name: 'handedness',
|
|
},
|
|
{
|
|
type: 'text',
|
|
title: 'How old are you?',
|
|
name: 'age',
|
|
isRequired: debug ? false : false,
|
|
inputType: 'number',
|
|
min: 18,
|
|
max: 100,
|
|
defaultValue: 18,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const main_experiment_timeline = [
|
|
instructions_1,
|
|
instructions_2,
|
|
die_roll_practice_trial,
|
|
pre_die_roll_info,
|
|
die_roll_trial,
|
|
instructions_3,
|
|
...captcha_trials,
|
|
pre_survey_info,
|
|
survey_1,
|
|
pre_manip_info,
|
|
survey_2,
|
|
debrief,
|
|
];
|
|
|
|
|
|
if (debug) {
|
|
timeline.push(survey_2);
|
|
timeline.push(...main_experiment_timeline);
|
|
}
|
|
|
|
if (!debug) {
|
|
timeline.push(pre_consent_info);
|
|
timeline.push(enter_fullscreen);
|
|
timeline.push(consent_form);
|
|
timeline.push(...main_experiment_timeline);
|
|
}
|
|
jsPsych.run(timeline);
|