";
// init the test
function init() {
$$$("inst").innerHTML =
"
" +
"
" +
"
Quick Instructions on How to Test Your Reaction Time
" +
"
" + 'In this test, you will be given a series of tasks in which you are to determine the changes in simple images of geometric shapes. You should try to react as quickly as possible on each trial. ' +
"" +
'Please maximize your browser window before you begin.' +
"
" +
"
" + "There will be a short practice trial before the actual test to familiarize you with what you will have to do. As you go along, you will receive detailed instructions on what to do for the different tasks. In all, the test should take about 5 minutes."
+ "
" + "
" +
"After the test, you will be presented some test results to show your average reaction time" + ". " +
// "You\'ll learn about how fast you react and how they compare to others with similar backgrounds to you!" + "
" +
"Note: You can also press [space] instead of clicking [next] to continue." +
"
";
// remove loader
$$$("ajax_working").style.display = "none";
$$$("next_button").style.opacity = '1';
showSlide("inst");
showNextButton(nextTrial);
}
// function to finish the experiment
function finish() {
// console.log(JSON.stringify(expment.results));
$.ajax({
type: 'POST',
url: 'includes/data.php',
data: {
participant_id: vars.participant_id,
participant_country: vars.participant_country,
results: JSON.stringify(expment.results),
exp_trial_count: expment.trialCounts["experiment"]["total"]
}
}).done( function (data) {
console.log("data=" + data);
if ((typeof data) == "string" && data.slice(0,6) == "Failed") {
// console.log("first");
handleError(data);
} else {
try {
results(JSON.parse(data));
}
catch (e) {
// console.log("second");
handleError("ERR: " + e.toString() + " DATA: " + data);
}
}
});
}
// processes the user's response, and records the data for the trial if necessary
function processResponse(input, trial) {
// if there's a chain of timeouts, clear them all
if(vars.ch)
clearChain(vars.ch);
// record data
// `response` should be either 0 (incorrect), 1 (correct), or
// -1 (don't know) for change detection tasks, and 1 for
// focal detection tasks.
expment.results[vars.phase].push({
task_type: trial.taskType,
response: input.response,
reaction_time: input.reaction_time,
color_changed: input.color_changed ? 1 : 0
});
// console.log(expment.results);
$.ajax({
type: 'POST',
url: 'includes/dropout.php',
data: {
participant_id: vars.participant_id,
trial_num: trial.num,
phase: vars.phase,
task_type: trial.taskType,
response: input.response,
reaction_time: input.reaction_time,
color_changed: input.color_changed
}
}).done(function() {});
// and this trial is done; go to the next trial.
nextTrial();
}
// The main function to handle running the trials.
// This contains the chain of events and the end of experiment functionality.
function nextTrial() {
var trial;
// Shift the next trial's information off the front of the trials array.
if (trial = expment.trials[vars.phase].shift()) {
// Randomize colors for change detection
var colorChoices;
if(trial.taskType !== 'focalDetection') {
// work with copy
colorChoices = arrayShuffle(config.changeDetectionSquareColors.slice());
for(var i = 0; i < vars.square_colors.length; i++) {
vars.square_colors[i] = colorChoices.shift();
}
}
// Change prompt to reflect instructions by task type
var focalDetectionName = 'Flashing Square';
var changeDetectionName = 'Changing Color';
var taskName = trial.taskType == 'focalDetection' ? focalDetectionName : changeDetectionName;
var focalDetectionTaskDirective = 'Hit the spacebar as soon as the cross changes into a square.';
var changeDetectionTaskDirective = 'Click on the square that changes color.';
var directive = trial.taskType == 'focalDetection' ? focalDetectionTaskDirective : changeDetectionTaskDirective;
var illustration = trial.taskType == 'focalDetection' ? "" : ""
$$$("prompt").innerHTML =
'
' +
"
" + taskName + "
" +
'Focus on the cross in the next screen. ' + directive + '
' + illustration + '
' +
'
' + 'Ready?' + '
' +
// '
' + 'Click here to proceed' + '
';
'';
// Show instructions, wait for user response
if (trial.type === "inst") {
$$$("progress").innerHTML = "";
$$$("heading").innerHTML = "";
switch (vars.phase) {
case 'practice':
$$$("inst").innerHTML =
"
" + 'Practice Trial' + "
" +
"
" + "Remember, this is only for practice. " +
'Your results will be discarded.' +
"
" +
"
" + 'You will receive instructions on what to do on each task. ' +
"Press next when you\'re ready to begin the practice. Get ready! You have to be fast!" + "
";
break;
case 'experiment':
$$$("inst").innerHTML =
"
" + 'Great Job! :)' + "
" +
"" +
"
" + "Now for the real deal. It\'ll be just like the practice you just took, but with more trials. Don\'t worry, it will just take a few minutes." +
"
" + "When you\'re ready, press next to begin the test." + "
";
break;
case 2:
$$$("inst").innerHTML =
"
" +
"
" + 'You\'re Doing Great!' + "
" +
"
" + "Time for a break! You\'re half-way there!" +
"
" + "When you\'re ready, press next to begin part 2, which is identical to the part you just completed." +
" " + "Note that some websites are repeated intentionally. Please rate them again honestly." + "
" +
"";
break;
}
showSlide("inst");
showNextButton(nextTrial);
} else {
// `response` should be either 0 (incorrect), 1 (correct), or
// -1 (don't know) for change detection tasks, and null for
// focal detection tasks.
var input = {
reaction_time: null,
response: null,
color_changed: null
}
// $$$("progress").innerHTML = progressHTML;
// do a trial here
var focalDetectionSequence = [
function () {
hideNextButton();
showSlide("empty");
$("#stims").children().hide();
$("#" + trial.stim_type + "_" + trial.stim_index).show();
if(vars.phase == 'practice') {
$$$("heading").innerHTML = "
";
}
// $$$("progress").innerHTML = "Progress: " + trial.num + " / " + expment.trialCounts[vars.phase]["total"];
$$$("progress").innerHTML = progressHTML;
$("#progressbar > div").css({
width: ((trial.num /
expment.trialCounts[vars.phase]["total"]) * 100) +
"%"
});
$$$('response-buttons').style.visibility = 'hidden';
}, config.itiDuration,
function () {
showSlide("task");
// Draw focus cross
var canvas = document.getElementById("task-canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, config.canvasWidth, config.canvasHeight);
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.moveTo(0.4 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.lineTo(0.6 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.moveTo(0.5 * config.canvasWidth, 0.4 * config.canvasHeight);
ctx.lineTo(0.5 * config.canvasWidth, 0.6 * config.canvasHeight);
ctx.stroke();
}, config.focusCrossDuration,
function () {
var canvas = document.getElementById("task-canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, config.canvasWidth, config.canvasHeight);
// Big cross
ctx.strokeStyle = config.frameCrossColor;
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.moveTo(0.2 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.lineTo(0.8 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.moveTo(0.5 * config.canvasWidth, 0.2 * config.canvasHeight);
ctx.lineTo(0.5 * config.canvasWidth, 0.8 * config.canvasHeight);
ctx.stroke();
// Squares
drawRect(ctx, "black", vars.square_colors[0], 0.2 * config.canvasWidth, 0.2 * config.canvasHeight, 0.1 * config.canvasWidth, 0.1 * config.canvasHeight);
drawRect(ctx, "black", vars.square_colors[1], 0.7 * config.canvasWidth, 0.2 * config.canvasHeight, 0.1 * config.canvasWidth, 0.1 * config.canvasHeight);
drawRect(ctx, "black", vars.square_colors[2], 0.2 * config.canvasWidth, 0.7 * config.canvasHeight, 0.1 * config.canvasWidth, 0.1 * config.canvasHeight);
drawRect(ctx, "black", vars.square_colors[3], 0.7 * config.canvasWidth, 0.7 * config.canvasHeight, 0.1 * config.canvasWidth, 0.1 * config.canvasHeight);
}, config.changeDetectionDuration,
function () {
trial.st = (new Date()) - trial.st;
// showSlide("empty");
var canvas = document.getElementById("task-canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, config.canvasWidth, config.canvasHeight);
}, config.changeDetectionBlankDuration,
function () {
// var rt_start = new Date();
// var input = { response: 0, rt: 0 };
// showSlide("task");
// $$$('response-buttons').innerHTML =
// "" +
// "";
$$$('response-buttons').style.visibility = 'visible';
var canvas = document.getElementById("task-canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, config.canvasWidth, config.canvasHeight);
// Big cross
ctx.strokeStyle = config.frameCrossColor;
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.moveTo(0.2 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.lineTo(0.8 * config.canvasWidth, 0.5 * config.canvasHeight);
ctx.moveTo(0.5 * config.canvasWidth, 0.2 * config.canvasHeight);
ctx.lineTo(0.5 * config.canvasWidth, 0.8 * config.canvasHeight);
ctx.stroke();
// Squares
var getRandomArbitary = function (min, max) {
return Math.random() * (max - min) + min;
}
var offset = function() {
var result;
switch(trial.taskType) {
case 'changeDetectionExpand':
result = 0.1 * config.canvasWidth;
break;
case 'changeDetectionShrink':
result = -0.1 * config.canvasWidth;
break;
case 'changeDetectionRandom':
result = getRandomArbitary(-0.1 * config.canvasWidth, 0.1 * config.canvasWidth);
// console.log(result);
break;
case 'changeDetectionSameLocation':
result = 0;
}
return result;
}
var shouldChangeSquare = Math.floor(Math.random() * 2) == 1;
var changedSquare = shouldChangeSquare ? Math.round((Math.random() * 10) % vars.square_colors.length - 1) : -1;
if(shouldChangeSquare) {
vars.square_colors[changedSquare] = colorChoices.shift();
// console.log("Square " + changedSquare + " changed");
}
var squareWidth = 0.1 * config.canvasWidth;
var squareHeight = 0.1 * config.canvasHeight;
var squareCoords = [
{
x: 0.2 * config.canvasWidth - offset(),
y: 0.2 * config.canvasHeight - offset()
},
{
x: 0.7 * config.canvasWidth + offset(),
y: 0.2 * config.canvasHeight - offset()
},
{
x: 0.2 * config.canvasWidth - offset(),
y: 0.7 * config.canvasHeight + offset()
},
{
x: 0.7 * config.canvasWidth + offset(),
y: 0.7 * config.canvasHeight + offset()
}
]
// console.log(squareCoords);
drawRect(ctx, "black", vars.square_colors[0], squareCoords[0].x, squareCoords[0].y, squareWidth, squareHeight);
drawRect(ctx, "black", vars.square_colors[1], squareCoords[1].x, squareCoords[1].y, squareWidth, squareHeight);
drawRect(ctx, "black", vars.square_colors[2], squareCoords[2].x, squareCoords[2].y, squareWidth, squareHeight);
drawRect(ctx, "black", vars.square_colors[3], squareCoords[3].x, squareCoords[3].y, squareWidth, squareHeight);
var rt_start = new Date();
input.color_changed = shouldChangeSquare;
// console.log(input.color_changed);
$('#no-change-btn').off('click').on('click', function() {
input.reaction_time = (new Date()) - rt_start;
// Check if there's actually no change
if(shouldChangeSquare == false) {
input.response = 1;
} else {
input.response = 0;
}
processResponse(input, trial);
});
$('#dont-know-btn').off('click').on('click', function() {
input.reaction_time = (new Date()) - rt_start;
input.response = -1;
processResponse(input, trial);
});
$(canvas).off('click.change-detection')
.on('click.change-detection', function(e) {
input.reaction_time = (new Date()) - rt_start;
var cursorPos = getCanvasCursorPosition(canvas,
e);
var x = cursorPos.x;
var y = cursorPos.y;
var hitSquareIndex;
for(var i = 0; i < squareCoords.length; i++) {
var hit = isPointInRect(x, y,
squareCoords[i].x,
squareCoords[i].y,
squareWidth,
squareHeight);
if(hit) {
hitSquareIndex = i;
}
}
if(hitSquareIndex !== undefined) {
$(canvas).off('click.change-detection');
// console.log(hitSquareIndex == changedSquare);
if(hitSquareIndex == changedSquare) {
// alert("Yep");
input.response = 1;
} else {
input.response = 0;
}
processResponse(input, trial);
}
});
}
];
$$$("progress").innerHTML = "";
$$$("heading").innerHTML = "";
showSlide("prompt");
hideNextButton();
var proceedWithTask = function() {
if(trial.taskType == 'focalDetection') {
vars.ch = chain.apply(this, focalDetectionSequence);
} else {
vars.ch = chain.apply(this, changeDetectionSequence)
}
}
$('#ready-proceed-button').on('click.prompt.proceed', function() {
$('#ready-proceed-button').off('click.prompt.proceed');
proceedWithTask();
});
// getKeyboardInput(["space"], function() {
// disableKeyboard();
// proceedWithTask();
// });
}
} else {
// not completely done yet? go on to next phase!
if (vars.phase == 'practice') {
vars.phase = 'experiment';
nextTrial();
return;
}
// else, finish the test
showCommentsPage();
// showDemographics();
}
}
function drawRect(ctx, stroke, fill, x, y, w, h) {
ctx.lineWidth = 1;
ctx.strokeStyle = stroke;
ctx.fillStyle = fill;
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.fill();
ctx.stroke();
}
function getCanvasCursorPosition(canvas, event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
return {x: x, y: y}
}
function isPointInRect(x, y, rx, ry, rw, rh) {
var inX = false;
var inY = false;
var bottomLeftX = rx;
var bottomLeftY = ry + rh;
var topRightX = rx + rw;
var topRightY = ry;
if(x > bottomLeftX && x < topRightX) {
inX = true;
}
if(y < bottomLeftY && y > topRightY) {
inY = true;
}
return (inX && inY);
}
// setup function
function setup() {
// force orientation
fixOrientation("landscape");
// first do a browser detect
var browserDetectResults = browserDetect();
withTouch = browserDetectResults.is_mobile;
if (!browserDetectResults.pass) {
$("#error").html(getBrowserErrorHTML(browserDetectResults));
showFailPage();
}
// set up html
$("#header").html("" +
'Tell your friends about this test!' + "");
$("#share_this").detach().appendTo("#header");
$("#share_this").show();
$("#header").append("
");
var why = 'We are trying to understand perceptual differences between people from varying cultural backgrounds. More specifically, the goal of this study is to find out the extent of the role our cultural background has in shaping the our visual perception.';
var what = 'You will be given tasks to identify as quickly as possible which square out of four, if any, changes color, and also tasks to respond as quickly as possible when a square appears on screen. Pressing the back button or leaving the test will cause all progress to be lost. You will receive more specific instructions shortly.';
var min = "5";
var details = 'If you have questions about your rights as a research participant, or wish to obtain information, ask questions or discuss any concerns about this study with someone other than the researcher(s), please contact the University of Washington Human Subjects Division at 206-543-0098 (for international calls include the US Calling Code: +1-206-543-0098).';
var study_info = "
"+'Welcome to the Reaction Time Study!'+"
"+'Please carefully read the following information.' + "
";
study_info += ""+'Why we are doing this research'+": "+why+"
";
study_info += ""+'What you will have to do'+": "+what+"
";
study_info += 'What you will get out of it: We will give you feedback on how your results compare to those of other participants. The final results from this experiment will be posted on our blog page. The experiment is not designed to benefit you, but you may enjoy it and enjoy comparing your results with those of other participants.'+"
";
study_info += 'Privacy and Data Collection: We will not ask you for your name. Any data that we collect will be securely stored on our servers.'+"
";
study_info += ""+'Length of the experiment:'+" "+min+" min
";
study_info += ""+'Contact information: ' +"" + 'If you have questions about this research, you may contact Professor Katharina Reinecke, Paul G. Allen Center for Computer Science & Engineering, Box 352350, Seattle, WA 98195, ' + "reinecke@cs.washington.edu
";
$('#task-canvas').prop({
width: config.canvasWidth,
height: config.canvasHeight
});
// Create all the trials
var taskTrials = {
practice: [],
experiment: []
}
for(phase in expment.trialCounts) {
var total = 0;
for(taskType in expment.trialCounts[phase]) {
for(var i = 0; i < expment.trialCounts[phase][taskType]; i++) {
taskTrials[phase].push(taskType);
total++;
}
}
expment.trialCounts[phase]["total"] = total;
}
// shuffle
for(phase in taskTrials) {
taskTrials[phase] = arrayShuffle(taskTrials[phase]);
}
// Populate the `trials` array
for(phase in taskTrials) {
// Add in an instruction trial as the first one
expment.trials[phase].push({
num: 0,
type: "inst"
});
for(var i = 0; i < taskTrials[phase].length; i++) {
expment.trials[phase].push({
num: i + 1,
type: "trial",
taskType: taskTrials[phase][i]
})
}
}
// preload all images
// var preloadArr = getCommonImageLinksArray().concat(["img/inst_stim.png", "img/inst_likert.png", "img/person_blue.png", "img/person_gray.png",
// "img/cute-cat.jpg"]);
// for (var i = 0; i < 2; i++) {
// for (var j = 0; j < stims[i].length; j++) {
// preloadArr.push(stims[i][j].url);
// }
// }
// load images into DOM in order to just hide and unhide stuff
// var preload_helper = function () {
// for (var i = 0; i < 2; i++) {
// for (var j = 0; j < stims[i].length; j++) {
// $("#stims").append("");
// }
// }
// // studyInfo();
// }
// preload(preloadArr, preload_helper);
// showDemographics();
// init();
studyInfo();
$.ajax({
type: 'POST',
url: 'includes/experiment_loaded.php',
data: {}
}).done(function() {});
}
// prevent accidental closing of test
window.onbeforeunload = function () {
return 'You are leaving the test, and all your progress on this test will be lost.';
}
// window loaded? let's set up!
window.onload = setup;