Merge pull request #1081 from GEJ1/instructions

Instructions plugin changes: add page_label parameter to plugin and docs, add show_page_number parameter to docs
This commit is contained in:
Becky Gilbert 2020-10-01 16:52:28 -07:00 committed by GitHub
commit 36df451b08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 348 additions and 6 deletions

View File

@ -2,10 +2,6 @@
This plugin is for showing instructions to the subject. It allows subjects to navigate through multiple pages of instructions at their own pace, recording how long the subject spends on each page. Navigation can be done using the mouse or keyboard. Subjects can be allowed to navigate forwards and backwards through pages, if desired.
## Parameters
Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
Parameter | Type | Default Value | Description
----------|------|---------------|------------
pages | array | *undefined* | Each element of the array is the content for a single page. Each page should be an HTML-formatted string.
@ -16,7 +12,8 @@ allow_keys | boolean | true | If true, the subject can use keyboard keys to navi
show_clickable_nav | boolean | false | If true, then a `Previous` and `Next` button will be displayed beneath the instructions. Subjects can click the buttons to navigate.
button_label_previous | string | 'Previous' | The text that appears on the button to go backwards.
button_label_next | string | 'Next' | The text that appears on the button to go forwards.
show_page_number | boolean | false | If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons.
page_label | string | 'Page' | The text that appears before x/y pages displayed with show_page_number.
## Data Generated

View File

@ -0,0 +1,43 @@
# jspsych-virtual-chin-rest plugin
This plugin consist in two parts:
To first calculate a participants display, participants are asked to place a credit card-sized card on the screen and adjust the slider on the screen to fit the credit card. That allows the researchers to calculate the pixel density on the monitor.
To measure the users distance from his or her monitor, there is also a blind spot task. Testers are asked to focus on a black square on the screen with their right eye closed, while a red dot repeatedly sweeps from right to left. They must hit the spacebar on their keyboards whenever it appears that the red dot has disappeared. That allows researchers to determine the distance between the center of the black square and the center of the red dot when it disappears from eyesight and understand how far the participant is from the monitor.
We would appreciate it if you cited this paper when you use the virtual-chin-rest plugin:
**Li, Q., Joo, S. J., Yeatman, J. D., & Reinecke, K. (2020). Controlling for Participants Viewing Distance in Large-Scale, Psychophysical Online Experiments Using a Virtual Chinrest. Scientific Reports, 10(1), 1-11. DOI: [10.1038/s41598-019-57204-1]**
## Parameters
Parameters can be left unspecified if the default value is acceptable.
|Parameter|Type|Default Value| Descripton|
|---------|----|-------------|-----------|
## Data Generated
In addition to the default data collected by all plugins, this plugin collects all parameter data described above and the following data for each trial.
|Name|Type|Value|
|----|----|-----|
viewing_distance_cm|numeric|
cardWidth_px| numeric
screen_size_px| char
## Example
```javascript
var chin = {
type: 'virtual-chin'
}

View File

@ -63,6 +63,12 @@ jsPsych.plugins.instructions = (function() {
default: false,
description: 'If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons.'
},
page_label: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Page label',
default: 'Page',
description: 'The text that appears before x/y (current/total) pages displayed with show_page_number'
},
button_label_previous: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Button label previous',
@ -104,7 +110,7 @@ jsPsych.plugins.instructions = (function() {
var pagenum_display = "";
if(trial.show_page_number) {
pagenum_display = "<span style='margin: 0 1em;' class='"+
"jspsych-instructions-pagenum'>Page "+(current_page+1)+"/"+trial.pages.length+"</span>";
"jspsych-instructions-pagenum'>"+ trial.page_label + ' ' +(current_page+1)+"/"+trial.pages.length+"</span>";
}
if (trial.show_clickable_nav) {

View File

@ -0,0 +1,296 @@
/*
* plugin for jsPsych based in Qisheng Li 11/2019. /// https://github.com/QishengLi/virtual_chinrest
Modified by Gustavo Juantorena 08/2020 // https://github.com/GEJ1
Work in progress in jsPsych discussion: https://github.com/jspsych/jsPsych/discussions/975
*/
jsPsych.plugins['virtual-chin'] = (function() {
var plugin = {};
plugin.info = {
name: "virtual-chin",
parameters: {
viewing_distance_cm: {
type: jsPsych.plugins.parameterType.INT,
default: 0
},
cardWidth_px: {
type: jsPsych.plugins.parameterType.INT,
default: 0
},
pixels_per_unit: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Pixels per unit',
default: 100,
description: 'After the scaling factor is applied, this many pixels will equal one unit of measurement.'
}
// prompt_card: {
// type: jsPsych.plugins.parameterType.STRING,
// default: null,
// description: 'Any content here will be displayed above card image.'
// },
// prompt_blindspot: {
// type: jsPsych.plugins.parameterType.STRING,
// default: null,
// description: 'Any content here will be displayed below the stimulus.'
// }
}
}
plugin.trial = function(display_element, trial) {
// Get screen size
var w = window.innerWidth;
var h = window.innerHeight;
const screen_size_px = []
screen_size_px.push(w)
screen_size_px.push('x')
screen_size_px.push(h)
// data saving
var trial_data = { //I need to modify this in order to save important data
'viewing_distance_cm': trial.viewing_distance_cm,
'cardWidth_px': trial.cardWidth_px,
'screen_size_px': trial.screen_size_px
};
trial_data.screen_size_px = screen_size_px
//Store all the configuration data in variable 'data'
var data = {"dataType":"configurationData"};
data["ballPosition"] = [];
data["sliderClicked"] = false;
(function ( distanceSetup, $ ) { // jQuery short-hand for $(document).ready(function() { ... });
distanceSetup.round = function(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
};
distanceSetup.px2mm = function(cardImageWidth) {
const cardWidth = 85.6; //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
var px2mm = cardImageWidth/cardWidth;
data["px2mm"] = distanceSetup.round(px2mm, 2);
return px2mm;
};
}( window.distanceSetup = window.distanceSetup || {}, jQuery));
function getCardWidth() {
var cardWidthPx = $('#card').width();
data["cardWidthPx"] = distanceSetup.round(cardWidthPx,2);
trial_data.cardWidth_px = cardWidthPx // add to trial_data
return cardWidthPx
}
function configureBlindSpot() {
drawBall();
$('#page-size').remove();
$('#blind-spot').css({'visibility':'visible'});
// $(document).on('keydown', recordPosition);
$(document).on('keydown', recordPosition);
};
$( function() {
$( "#slider" ).slider({value:"50"});
} );
$(document).ready(function() {
$( "#slider" ).on("slide", function (event, ui) {
var cardWidth = ui.value + "%";
$("#card").css({"width":cardWidth});
});
$('#slider').on('slidechange', function(event, ui){
data["sliderClicked"] = true;
});
});
//=============================
//Ball Animation
function drawBall(pos=180){
// pos: define where the fixation square should be.
var mySVG = SVG("svgDiv");
const cardWidthPx = getCardWidth()
const rectX = distanceSetup.px2mm(cardWidthPx)*pos;
const ballX = rectX*0.6 // define where the ball is
var ball = mySVG.circle(30).move(ballX, 50).fill("#f00");
window.ball = ball;
var square = mySVG.rect(30, 30).move(Math.min(rectX - 50, 950), 50); //square position
data["squarePosition"] = distanceSetup.round(square.cx(),2);
data['rectX'] = rectX
data['ballX'] = ballX
};
function animateBall(){
ball.animate(7000).during(
function(pos){
moveX = - pos*data['ballX'];
window.moveX = moveX;
moveY = 0;
ball.attr({transform:"translate("+moveX+","+moveY+")"});
}
).loop(true, false).
after(function(){
animateBall();
});
//disbale the button after clicked once.
$("#start").attr("disabled", true);
};
function recordPosition(event, angle=13.5) {
// angle: define horizontal blind spot entry point position in degrees.
if (event.keyCode == '32') { //Press "Space"
data["ballPosition"].push(distanceSetup.round((ball.cx() + moveX),2));
var sum = data["ballPosition"].reduce((a, b) => a + b, 0);
var ballPosLen = data["ballPosition"].length;
data["avgBallPos"] = distanceSetup.round(sum/ballPosLen, 2);
var ball_sqr_distance = (data["squarePosition"]-data["avgBallPos"])/data["px2mm"];
var viewDistance = ball_sqr_distance/Math.radians(angle)
data["viewDistance_mm"] = distanceSetup.round(viewDistance, 2);
//counter and stop
var counter = Number($('#click').text());
counter = counter - 1;
$('#click').text(Math.max(counter, 0));
if (counter <= 0) {
ball.stop();
// Disable space key
$('html').bind('keydown', function(e)
{
if (e.keyCode == 32) {return false;} //32 is spacebar I CHANGE THAT
});
// Display data
$('#info').css("visibility", "visible");
$('#info-h').append(data["viewDistance_mm"]/10)
//Estimated viewing distance in centimeters
trial_data.viewing_distance_cm = (data["viewDistance_mm"]/10); // add to trial_data
dist = Math.round(data["viewDistance_mm"]/10)
// The trial must end
end_trial()
return trial_data.viewing_distance_cm;
}
ball.stop();
animateBall();
}
}
//helper function for radians
// Converts from degrees to radians.
Math.radians = function(degrees) {
return degrees * Math.PI / 180;
};
// You can write functions here that live only in the scope of plugin.trial
function show_stimulus(){
// create html for display
var html = "<body><div id='content'><div id='page-size'><br><br><br><br><br><br>";
// html += "<h3> Lets find out what your monitor size is (click to go into <div onclick='fullScreen(); registerClick();' style='display:inline; cursor:pointer; color: red'><em><u>full screen mode</u></em></div>).</h2>";
html += "<p>Please use any credit card that you have available (it can also be a grocery store membership card, your drivers license, or anything that is of the same format), hold it onto the screen, and adjust the slider below to its size.</p>";
html += "<p>(If you don't have access to a real card, you can use a ruler to measure image width to 3.37inch or 85.6mm, or make your best guess!)</p>";
html += "<b style='font-style: italic'>Make sure you put the card onto your screen.</b>";
html += '<br><div id="container">';
html += "<div id='slider'></div>";
html += '<br> <img id="card" src="card.png" style="width: 50%"><br><br>';
html +='<button id="btnBlindSpot" class="btn btn-primary">Click here when you are done!</button></div></div>';
html += '<div id="blind-spot" style="visibility: hidden">';
html += '<!-- <h2 class="bolded-blue">Task 2: Wheres your blind spot?</h2> -->';
html += "<h3>Now, let's quickly test how far away you are sitting.</h3>";
html += "<p>You might know that vision tests at a doctor's practice often involve chinrests; the doctor basically asks you to sit away from a screen in a specific distance. We do this here with a 'virtual chinrest'.</p>";
html += '<h3>Instructions</h3>';
html += '<p>1. Put your finger on <b>space bar</b> on the keyboard.</p>';
html += '<p>2. Close your right eye. <em>(Tips: it might be easier to cover your right eye by hand!)</em></p>';
html += '<p>3. Using your left eye, focus on the black square.</p>';
html += '<p>4. Click the button below to start the animation of the red ball. The <b style="color: red">red ball</b> will disappear as it moves from right to left. Press the Space key as soon as the ball disappears from your eye sight.</p><br>';
html += '<p>Please do it <b>five</b> times. Keep your right eye closed and hit the Space key fast!</p><br>';
html += '<button class="btn btn-primary" id="start" ">Start</button>';
html += '<div id="svgDiv" style="width:1000px;height:200px;"></div>';
html += "Hit 'space' <div id='click' style='display:inline; color: red; font-weight: bold'>5</div> more times!</div>";
// render
display_element.innerHTML = html;
//Event listeners for buttons
document.getElementById("btnBlindSpot").addEventListener('click', function() {
configureBlindSpot();
});
document.getElementById("start").addEventListener('click', function() {
animateBall();
});
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response, // we need to create after_response
valid_responses: [trial.key], // valid_responses expects an array
rt_method: 'performance', // This is only relevant for RT in audio stimuli
persist: false, // true if you want to listen to more than one key
allow_held_key: true // false for a new key pressing in order to get a new response
});
}
function after_response(response_info){
// rt.push(response_info.rt); // response time of the key
end_trial();
}
function end_trial(){
jsPsych.finishTrial(trial_data); // ends trial and save the data
// display_element.innerHTML = ' '; // clear the display
jsPsych.pluginAPI.cancelAllKeyboardResponses();
}
show_stimulus();
};
return plugin;
})();