mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-12 16:48:12 +00:00
Merge branch 'gh-pages' of github.com:jodeleeuw/jsPsych into gh-pages
This commit is contained in:
commit
f3730f8c1c
211
examples/css/jspsych.css
Normal file
211
examples/css/jspsych.css
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* CSS for jsPsych experiments.
|
||||||
|
*
|
||||||
|
* This stylesheet provides minimal styling to make jsPsych
|
||||||
|
* experiments look polished without any additional styles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* fonts and type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import url(//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: 'Open Sans', 'Arial', sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.very-small {
|
||||||
|
font-size: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.very-large {
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Classes for changing location of things
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Form elements like input fields and buttons
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
font-family: 'Open Sans', 'Arial', sans-sefif;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: #eaeaea;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
color: #333;
|
||||||
|
font-family: 'Open Sans', 'Arial', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
border:1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Container holding jsPsych content
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
.jspsych-display-element {
|
||||||
|
width: 800px;
|
||||||
|
margin: 50px auto 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-animation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-animation-image {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-categorize-animation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-categorize-animation-stimulus {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-categorize
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-categorize-stimulus {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-free-sort
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-free-sort-arena {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border: 2px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-palmer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-palmer-raphaelCanvas {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-same-different
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.jspsych-same-different-stimulus {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-single-stim
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#jspsych-single-stim-stimulus {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-xab
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.jspsych-xab-stimulus {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* PLUGIN: jspsych-survey-text
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.jspsych-survey-text {
|
||||||
|
margin: 0.25em 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jspsych-survey-text-question {
|
||||||
|
margin: 2em 0em;
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
// jspsych.js
|
/**
|
||||||
//
|
* jspsych.js
|
||||||
// Josh de Leeuw
|
* Josh de Leeuw
|
||||||
// Percepts and Concepts Lab, Indiana University
|
*
|
||||||
//
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki
|
||||||
//
|
*
|
||||||
|
**/
|
||||||
(function($) {
|
(function($) {
|
||||||
jsPsych = (function() {
|
jsPsych = (function() {
|
||||||
|
|
||||||
@ -28,9 +29,7 @@
|
|||||||
var DOM_target;
|
var DOM_target;
|
||||||
// time that the experiment began
|
// time that the experiment began
|
||||||
var exp_start_time;
|
var exp_start_time;
|
||||||
// turk info
|
|
||||||
var turk_info;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// public methods
|
// public methods
|
||||||
//
|
//
|
||||||
@ -75,9 +74,13 @@
|
|||||||
|
|
||||||
// import options
|
// import options
|
||||||
opts = $.extend({}, defaults, options);
|
opts = $.extend({}, defaults, options);
|
||||||
|
|
||||||
// set target
|
// set target
|
||||||
DOM_target = opts.display_element;
|
DOM_target = opts.display_element;
|
||||||
|
|
||||||
|
// add CSS class to DOM_target
|
||||||
|
DOM_target.addClass('jspsych-display-element');
|
||||||
|
|
||||||
run();
|
run();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,33 +89,17 @@
|
|||||||
// if flatten is true, then the hierarchical structure of the data
|
// if flatten is true, then the hierarchical structure of the data
|
||||||
// is removed and each array entry will be a single trial.
|
// is removed and each array entry will be a single trial.
|
||||||
|
|
||||||
core.data = function(flatten) {
|
core.data = function() {
|
||||||
var all_data = [];
|
var all_data = [];
|
||||||
|
|
||||||
for (var i = 0; i < exp_blocks.length; i++) {
|
for (var i = 0; i < exp_blocks.length; i++) {
|
||||||
all_data[i] = exp_blocks[i].data;
|
all_data[i] = exp_blocks[i].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(flatten===true){
|
|
||||||
all_data = flattenData(all_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return all_data;
|
return all_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// core.dataAsCSV returns a CSV string that contains all of the data
|
|
||||||
// append_data is an option map object that will append values
|
|
||||||
// to every row. for example, if append_data = {"subject": 4},
|
|
||||||
// then a column called subject will be added to the data and
|
|
||||||
// it will always have the value 4.
|
|
||||||
core.dataAsCSV = function(append_data) {
|
|
||||||
var dataObj = core.data();
|
|
||||||
return JSON2CSV(flattenData(dataObj, append_data));
|
|
||||||
};
|
|
||||||
|
|
||||||
core.saveCSVdata = function(filename, append_data) {
|
|
||||||
var data_string = core.dataAsCSV(append_data);
|
|
||||||
saveTextToFile(data_string, filename);
|
|
||||||
};
|
|
||||||
|
|
||||||
// core.progress returns an object with the following properties
|
// core.progress returns an object with the following properties
|
||||||
// total_blocks: the number of total blocks in the experiment
|
// total_blocks: the number of total blocks in the experiment
|
||||||
@ -153,6 +140,12 @@
|
|||||||
return exp_start_time;
|
return exp_start_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// core.totalTime() returns the length of time in ms since the experiment began
|
||||||
|
|
||||||
|
core.totalTime = function() {
|
||||||
|
return (new Date()).getTime() - exp_start_time.getTime();
|
||||||
|
};
|
||||||
|
|
||||||
// core.preloadImage will load images into the browser cache so that they appear quickly when
|
// core.preloadImage will load images into the browser cache so that they appear quickly when
|
||||||
// used during a trial.
|
// used during a trial.
|
||||||
// images: array of paths to images
|
// images: array of paths to images
|
||||||
@ -160,124 +153,33 @@
|
|||||||
// callback_load: a function with a single argument that calls whenever an image is loaded
|
// callback_load: a function with a single argument that calls whenever an image is loaded
|
||||||
// argument is the number of images currently loaded.
|
// argument is the number of images currently loaded.
|
||||||
|
|
||||||
core.preloadImages = function(images, callback_complete, callback_load){
|
core.preloadImages = function(images, callback_complete, callback_load) {
|
||||||
|
|
||||||
// flatten the images array
|
// flatten the images array
|
||||||
images = flatten(images);
|
images = flatten(images);
|
||||||
|
|
||||||
var n_loaded = 0;
|
|
||||||
var loadfn = (typeof callback_load === 'undefined') ? function(){} : callback_load;
|
|
||||||
var finishfn = (typeof callback_complete === 'undefined') ? function(){} : callback_complete;
|
|
||||||
|
|
||||||
for(var i=0;i<images.length;i++){
|
|
||||||
var img = new Image();
|
|
||||||
|
|
||||||
img.onload = function(){
|
|
||||||
n_loaded++;
|
|
||||||
loadfn(n_loaded);
|
|
||||||
if(n_loaded == images.length){
|
|
||||||
finishfn();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
img.src = images[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// core.turkInfo gets information relevant to mechanical turk experiments. returns an object
|
|
||||||
// containing the workerID, assignmentID, and hitID, and whether or not the HIT is in
|
|
||||||
// preview mode, meaning that they haven't accepted the HIT yet.
|
|
||||||
core.turkInfo = function(force_refresh)
|
|
||||||
{
|
|
||||||
// default value is false
|
|
||||||
force_refresh = (typeof force_refresh === 'undefined') ? false : force_refresh;
|
|
||||||
// if we already have the turk_info and force_refresh is false
|
|
||||||
// then just return the cached version.
|
|
||||||
if(typeof turk_info !== 'undefined' && !force_refresh) {
|
|
||||||
return turk_info;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var turk = {};
|
|
||||||
|
|
||||||
var param = function(url, name ) {
|
var n_loaded = 0;
|
||||||
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
|
var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load;
|
||||||
var regexS = "[\\?&]"+name+"=([^&#]*)";
|
var finishfn = (typeof callback_complete === 'undefined') ? function() {} : callback_complete;
|
||||||
var regex = new RegExp( regexS );
|
|
||||||
var results = regex.exec( url );
|
for (var i = 0; i < images.length; i++) {
|
||||||
return ( results == null ) ? "" : results[1];
|
var img = new Image();
|
||||||
|
|
||||||
|
img.onload = function() {
|
||||||
|
n_loaded++;
|
||||||
|
loadfn(n_loaded);
|
||||||
|
if (n_loaded == images.length) {
|
||||||
|
finishfn();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var src = param(window.location.href, "assignmentId") ? window.location.href : document.referrer;
|
img.src = images[i];
|
||||||
|
|
||||||
var keys = ["assignmentId","hitId","workerId"];
|
|
||||||
keys.map(
|
|
||||||
function(key) {
|
|
||||||
turk[key] = unescape(param(src, key));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
turk.previewMode = (turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE");
|
|
||||||
|
|
||||||
turk.outsideTurk = (!turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "")
|
|
||||||
|
|
||||||
turk_info = turk;
|
|
||||||
|
|
||||||
return turk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
core.getDisplayElement = function() {
|
||||||
//
|
return DOM_target;
|
||||||
// These are public functions, intended to be used for developing plugins.
|
|
||||||
// They aren't considered part of the normal API for the core library.
|
|
||||||
//
|
|
||||||
|
|
||||||
core.normalizeTrialVariables = function(trial, protect){
|
|
||||||
|
|
||||||
protect = (typeof protect === 'undefined') ? [] : protect;
|
|
||||||
|
|
||||||
var keys = getKeys(trial);
|
|
||||||
|
|
||||||
var tmp = {};
|
|
||||||
for(var i=0; i<keys.length; i++){
|
|
||||||
|
|
||||||
var process = true;
|
|
||||||
for(var j = 0; j < protect.length; j++){
|
|
||||||
if(protect[j] == keys[i]){
|
|
||||||
process = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof trial[keys[i]] == "function" && process){
|
|
||||||
tmp[keys[i]] = trial[keys[i]].call();
|
|
||||||
} else {
|
|
||||||
tmp[keys[i]] = trial[keys[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if possible_array is not an array, then return a one-element array
|
|
||||||
// containing possible_array
|
|
||||||
core.enforceArray = function(params, possible_arrays) {
|
|
||||||
|
|
||||||
// function to check if something is an array, fallback
|
|
||||||
// to string method if browser doesn't support Array.isArray
|
|
||||||
var ckArray = Array.isArray || function(a) {
|
|
||||||
return toString.call(a) == '[object Array]';
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var i=0; i<possible_arrays.length; i++){
|
|
||||||
params[possible_arrays[i]] = ckArray(params[possible_arrays[i]]) ? params[possible_arrays[i]] : [params[possible_arrays[i]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// private functions //
|
// private functions //
|
||||||
@ -288,7 +190,14 @@
|
|||||||
|
|
||||||
// iterate through block list to create trials
|
// iterate through block list to create trials
|
||||||
for (var i = 0; i < exp_blocks.length; i++) {
|
for (var i = 0; i < exp_blocks.length; i++) {
|
||||||
var trials = jsPsych[opts["experiment_structure"][i]["type"]]["create"].call(null, opts["experiment_structure"][i]);
|
|
||||||
|
// check to make sure plugin is loaded
|
||||||
|
var plugin_name = opts.experiment_structure[i].type;
|
||||||
|
if (typeof jsPsych[plugin_name] == 'undefined') {
|
||||||
|
throw new Error("Failed attempt to create trials using plugin type " + plugin_name + ". Is the plugin loaded?");
|
||||||
|
}
|
||||||
|
|
||||||
|
var trials = jsPsych[plugin_name]["create"].call(null, opts["experiment_structure"][i]);
|
||||||
|
|
||||||
exp_blocks[i] = createBlock(trials);
|
exp_blocks[i] = createBlock(trials);
|
||||||
}
|
}
|
||||||
@ -364,6 +273,102 @@
|
|||||||
jsPsych[trial.type]["trial"].call(this, DOM_target, block, trial, 1);
|
jsPsych[trial.type]["trial"].call(this, DOM_target, block, trial, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return core;
|
||||||
|
})();
|
||||||
|
|
||||||
|
jsPsych.dataAPI = (function() {
|
||||||
|
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
// core.dataAsCSV returns a CSV string that contains all of the data
|
||||||
|
// append_data is an option map object that will append values
|
||||||
|
// to every row. for example, if append_data = {"subject": 4},
|
||||||
|
// then a column called subject will be added to the data and
|
||||||
|
// it will always have the value 4.
|
||||||
|
module.dataAsCSV = function(append_data) {
|
||||||
|
var dataObj = jsPsych.data();
|
||||||
|
return JSON2CSV(flattenData(dataObj, append_data));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.localSave = function(filename, format, append_data) {
|
||||||
|
|
||||||
|
var data_string;
|
||||||
|
|
||||||
|
if(format == 'JSON' || format == 'json') {
|
||||||
|
data_string = JSON.stringify(flattenData(jsPsych.data(), append_data));
|
||||||
|
} else if(format == 'CSV' || format == 'csv') {
|
||||||
|
data_string = module.dataAsCSV(append_data);
|
||||||
|
} else {
|
||||||
|
throw new Error('invalid format specified for jsPsych.dataAPI.localSave');
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTextToFile(data_string, filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.getTrialsOfType = function(trial_type){
|
||||||
|
var data = jsPsych.data();
|
||||||
|
|
||||||
|
data = flatten(data);
|
||||||
|
|
||||||
|
var trials = [];
|
||||||
|
for(var i = 0; i < data.length; i++){
|
||||||
|
if(data[i].trial_type == trial_type){
|
||||||
|
trials.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trials;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.displayData = function(format) {
|
||||||
|
format = (typeof format === 'undefined') ? "json" : format.toLowerCase();
|
||||||
|
if(format != "json" && format != "csv") {
|
||||||
|
console.log('Invalid format declared for displayData function. Using json as default.');
|
||||||
|
format = "json";
|
||||||
|
}
|
||||||
|
|
||||||
|
var data_string;
|
||||||
|
|
||||||
|
if(format == 'json') {
|
||||||
|
data_string = JSON.stringify(flattenData(jsPsych.data()), undefined, 1);
|
||||||
|
} else {
|
||||||
|
data_string = module.dataAsCSV();
|
||||||
|
}
|
||||||
|
|
||||||
|
var display_element = jsPsych.getDisplayElement();
|
||||||
|
|
||||||
|
display_element.append($('<pre>', {
|
||||||
|
html: data_string
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// private function to save text file on local drive
|
||||||
|
function saveTextToFile(textstr, filename) {
|
||||||
|
var blobToSave = new Blob([textstr], {
|
||||||
|
type: 'text/plain'
|
||||||
|
});
|
||||||
|
var blobURL = "";
|
||||||
|
if (typeof window.webkitURL !== 'undefined') {
|
||||||
|
blobURL = window.webkitURL.createObjectURL(blobToSave);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
blobURL = window.URL.createObjectURL(blobToSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
var display_element = jsPsych.getDisplayElement();
|
||||||
|
|
||||||
|
display_element.append($('<a>', {
|
||||||
|
id: 'jspsych-download-as-text-link',
|
||||||
|
href: blobURL,
|
||||||
|
css: {
|
||||||
|
display: 'none'
|
||||||
|
},
|
||||||
|
download: filename,
|
||||||
|
html: 'download file'
|
||||||
|
}));
|
||||||
|
$('#jspsych-download-as-text-link')[0].click();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// A few helper functions to handle data format conversion
|
// A few helper functions to handle data format conversion
|
||||||
//
|
//
|
||||||
@ -383,7 +388,7 @@
|
|||||||
|
|
||||||
return trials;
|
return trials;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function based on code suggested by StackOverflow users:
|
// this function based on code suggested by StackOverflow users:
|
||||||
// http://stackoverflow.com/users/64741/zachary
|
// http://stackoverflow.com/users/64741/zachary
|
||||||
// http://stackoverflow.com/users/317/joseph-sturtevant
|
// http://stackoverflow.com/users/317/joseph-sturtevant
|
||||||
@ -398,7 +403,7 @@
|
|||||||
for (var key in array[j]) {
|
for (var key in array[j]) {
|
||||||
var keyString = key + "";
|
var keyString = key + "";
|
||||||
keyString = '"' + keyString.replace(/"/g, '""') + '",';
|
keyString = '"' + keyString.replace(/"/g, '""') + '",';
|
||||||
if ($.inArray(key,columns) == -1) {
|
if ($.inArray(key, columns) == -1) {
|
||||||
columns[i] = key;
|
columns[i] = key;
|
||||||
line += keyString;
|
line += keyString;
|
||||||
i++;
|
i++;
|
||||||
@ -423,53 +428,519 @@
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
jsPsych.turk = (function() {
|
||||||
|
|
||||||
|
// turk info
|
||||||
|
var turk_info;
|
||||||
|
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
// core.turkInfo gets information relevant to mechanical turk experiments. returns an object
|
||||||
|
// containing the workerID, assignmentID, and hitID, and whether or not the HIT is in
|
||||||
|
// preview mode, meaning that they haven't accepted the HIT yet.
|
||||||
|
module.turkInfo = function(force_refresh) {
|
||||||
|
// default value is false
|
||||||
|
force_refresh = (typeof force_refresh === 'undefined') ? false : force_refresh;
|
||||||
|
// if we already have the turk_info and force_refresh is false
|
||||||
|
// then just return the cached version.
|
||||||
|
if (typeof turk_info !== 'undefined' && !force_refresh) {
|
||||||
|
return turk_info;
|
||||||
|
} else {
|
||||||
|
|
||||||
function saveTextToFile(textstr, filename) {
|
var turk = {};
|
||||||
var blobToSave = new Blob([textstr], {
|
|
||||||
type: 'text/plain'
|
var param = function(url, name) {
|
||||||
});
|
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
|
||||||
var blobURL = "";
|
var regexS = "[\\?&]" + name + "=([^&#]*)";
|
||||||
if (typeof window.webkitURL !== 'undefined') {
|
var regex = new RegExp(regexS);
|
||||||
blobURL = window.webkitURL.createObjectURL(blobToSave);
|
var results = regex.exec(url);
|
||||||
|
return (results == null) ? "" : results[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
var src = param(window.location.href, "assignmentId") ? window.location.href : document.referrer;
|
||||||
|
|
||||||
|
var keys = ["assignmentId", "hitId", "workerId", "turkSubmitTo"];
|
||||||
|
keys.map(
|
||||||
|
|
||||||
|
function(key) {
|
||||||
|
turk[key] = unescape(param(src, key));
|
||||||
|
});
|
||||||
|
|
||||||
|
turk.previewMode = (turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE");
|
||||||
|
|
||||||
|
turk.outsideTurk = (!turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "")
|
||||||
|
|
||||||
|
turk_info = turk;
|
||||||
|
|
||||||
|
return turk;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
blobURL = window.URL.createObjectURL(blobToSave);
|
};
|
||||||
|
|
||||||
|
// core.submitToTurk will submit a MechanicalTurk ExternalHIT type
|
||||||
|
|
||||||
|
module.submitToTurk = function(data) {
|
||||||
|
|
||||||
|
var turkInfo = core.turkInfo();
|
||||||
|
var assignmentId = turkInfo.assignmentId;
|
||||||
|
var turkSubmitTo = turkInfo.turkSubmitTo;
|
||||||
|
|
||||||
|
if (!assignmentId || !turkSubmitTo) return;
|
||||||
|
|
||||||
|
var dataString = [];
|
||||||
|
|
||||||
|
for (var key in data) {
|
||||||
|
|
||||||
|
if (data.hasOwnProperty(key)) {
|
||||||
|
dataString.push(key + "=" + escape(data[key]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DOM_target.append($('<a>', {
|
|
||||||
id: 'jspsych-download-as-text-link',
|
dataString.push("assignmentId=" + assignmentId);
|
||||||
href: blobURL,
|
|
||||||
css: {
|
var url = turkSubmitTo + "/mturk/externalSubmit?" + dataString.join("&");
|
||||||
display: 'none'
|
|
||||||
},
|
window.location.href = url;
|
||||||
download: filename,
|
|
||||||
html: 'download file'
|
|
||||||
}));
|
|
||||||
$('#jspsych-download-as-text-link')[0].click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
jsPsych.randomization = (function() {
|
||||||
|
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.repeat = function(array, repetitions, unpack) {
|
||||||
|
|
||||||
|
var arr_isArray = Array.isArray(array);
|
||||||
|
var rep_isArray = Array.isArray(repetitions);
|
||||||
|
|
||||||
|
// if array is not an array, then we just repeat the item
|
||||||
|
if(!arr_isArray){
|
||||||
|
if(!rep_isArray) {
|
||||||
|
array = [array];
|
||||||
|
repetitions = [repetitions];
|
||||||
|
} else {
|
||||||
|
repetitions = [repetitions[0]];
|
||||||
|
console.log('Unclear parameters given to randomizeSimpleSample. Multiple set sizes specified, but only one item exists to sample. Proceeding using the first set size.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!rep_isArray) {
|
||||||
|
var reps = [];
|
||||||
|
for(var i = 0; i < array.length; i++){
|
||||||
|
reps.push(repetitions);
|
||||||
|
}
|
||||||
|
repetitions = reps;
|
||||||
|
} else {
|
||||||
|
if(array.length != repetitions.length) {
|
||||||
|
// throw warning if repetitions is too short,
|
||||||
|
// throw warning if too long, and then use the first N
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be clear at this point to assume that array and repetitions are arrays with == length
|
||||||
|
var allsamples = [];
|
||||||
|
for(var i = 0; i < array.length; i++){
|
||||||
|
for(var j = 0; j < repetitions[i]; j++){
|
||||||
|
allsamples.push(array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = shuffle(allsamples);
|
||||||
|
|
||||||
|
if(unpack) { out = unpackArray(out); }
|
||||||
|
|
||||||
|
return shuffle(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.factorial = function(factors, repetitions, unpack){
|
||||||
|
|
||||||
|
var factorNames = Object.keys(factors);
|
||||||
|
|
||||||
|
var factor_combinations = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < factors[factorNames[0]].length; i++){
|
||||||
|
factor_combinations.push({});
|
||||||
|
factor_combinations[i][factorNames[0]] = factors[factorNames[0]][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 1; i< factorNames.length; i++){
|
||||||
|
var toAdd = factors[factorNames[i]];
|
||||||
|
var n = factor_combinations.length;
|
||||||
|
for(var j = 0; j < n; j++){
|
||||||
|
var base = factor_combinations[j];
|
||||||
|
for(var k = 0; k < toAdd.length; k++){
|
||||||
|
var newpiece = {};
|
||||||
|
newpiece[factorNames[i]] = toAdd[k];
|
||||||
|
factor_combinations.push($.extend({}, base, newpiece));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
factor_combinations.splice(0,n);
|
||||||
|
}
|
||||||
|
|
||||||
|
repetitions = (typeof repetitions === 'undefined') ? 1 : repetitions;
|
||||||
|
var with_repetitions = module.repeat(factor_combinations, repetitions, unpack);
|
||||||
|
|
||||||
|
return with_repetitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpackArray(array) {
|
||||||
|
|
||||||
|
var out = {};
|
||||||
|
|
||||||
|
for(var i = 0; i < array.length; i++){
|
||||||
|
var keys = Object.keys(array[i]);
|
||||||
|
for(var k = 0; k < keys.length; k++){
|
||||||
|
if(typeof out[keys[k]] === 'undefined') {
|
||||||
|
out[keys[k]] = [];
|
||||||
|
}
|
||||||
|
out[keys[k]].push(array[i][keys[k]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffle(array) {
|
||||||
|
var m = array.length, t, i;
|
||||||
|
|
||||||
|
// While there remain elements to shuffle…
|
||||||
|
while (m) {
|
||||||
|
|
||||||
|
// Pick a remaining element…
|
||||||
|
i = Math.floor(Math.random() * m--);
|
||||||
|
|
||||||
|
// And swap it with the current element.
|
||||||
|
t = array[m];
|
||||||
|
array[m] = array[i];
|
||||||
|
array[i] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
jsPsych.pluginAPI = (function() {
|
||||||
|
|
||||||
|
// keyboard listeners
|
||||||
|
var keyboard_listeners = [];
|
||||||
|
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.getKeyboardResponse = function(callback_function, valid_responses, rt_method, persist) {
|
||||||
|
|
||||||
|
rt_method = (typeof rt_method === 'undefined') ? 'date' : rt_method;
|
||||||
|
if (rt_method != 'date' && rt_method != 'performance') {
|
||||||
|
console.log('Invalid RT method specified in getKeyboardResponse. Defaulting to "date" method.');
|
||||||
|
rt_method = 'date';
|
||||||
|
}
|
||||||
|
|
||||||
|
var start_time;
|
||||||
|
if (rt_method == 'date') {
|
||||||
|
start_time = (new Date()).getTime();
|
||||||
|
}
|
||||||
|
if (rt_method == 'performance') {
|
||||||
|
start_time = performance.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener_id;
|
||||||
|
|
||||||
|
var listener_function = function(e) {
|
||||||
|
|
||||||
|
var key_time;
|
||||||
|
if (rt_method == 'date') {
|
||||||
|
key_time = (new Date()).getTime();
|
||||||
|
}
|
||||||
|
if (rt_method == 'performance') {
|
||||||
|
key_time = performance.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
var valid_response = false;
|
||||||
|
if (typeof valid_responses === 'undefined' || valid_responses.length === 0) {
|
||||||
|
valid_response = true;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < valid_responses.length; i++) {
|
||||||
|
if (typeof valid_responses[i] == 'string') {
|
||||||
|
if(typeof keylookup[valid_responses[i]] !== 'undefined'){
|
||||||
|
if(e.which == keylookup[valid_responses[i]]) {
|
||||||
|
valid_response = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid key string specified for getKeyboardResponse');
|
||||||
|
}
|
||||||
|
} else if (e.which == valid_responses[i]) {
|
||||||
|
valid_response = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid_response) {
|
||||||
|
|
||||||
|
var after_up = function(up) {
|
||||||
|
|
||||||
|
if(up.which == e.which) {
|
||||||
|
$(document).off('keyup', after_up);
|
||||||
|
|
||||||
|
if($.inArray(listener_id, keyboard_listeners) > -1) {
|
||||||
|
|
||||||
|
if(!persist){
|
||||||
|
// remove keyboard listener
|
||||||
|
module.cancelKeyboardResponse(listener_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_function({
|
||||||
|
key: e.which,
|
||||||
|
rt: key_time - start_time
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).keyup(after_up);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).keydown(listener_function);
|
||||||
|
|
||||||
|
// create listener id object
|
||||||
|
listener_id = {type: 'keydown', fn: listener_function};
|
||||||
|
|
||||||
|
// add this keyboard listener to the list of listeners
|
||||||
|
keyboard_listeners.push(listener_id);
|
||||||
|
|
||||||
|
return listener_id;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
module.cancelKeyboardResponse = function(listener) {
|
||||||
|
// remove the listener from the doc
|
||||||
|
$(document).off(listener.type, listener.fn);
|
||||||
|
|
||||||
|
// remove the listener from the list of listeners
|
||||||
|
if($.inArray(listener, keyboard_listeners) > -1) {
|
||||||
|
keyboard_listeners.splice($.inArray(listener, keyboard_listeners), 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.cancelAllKeyboardResponses = function() {
|
||||||
|
for(var i = 0; i< keyboard_listeners.length; i++){
|
||||||
|
$(document).off(keyboard_listeners[i].type, keyboard_listeners[i].fn);
|
||||||
|
}
|
||||||
|
keyboard_listeners = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// keycode lookup associative array
|
||||||
|
var keylookup = {
|
||||||
|
'backspace': 8,
|
||||||
|
'tab': 9,
|
||||||
|
'enter': 13,
|
||||||
|
'shift': 16,
|
||||||
|
'ctrl': 17,
|
||||||
|
'alt': 18,
|
||||||
|
'pause': 19,
|
||||||
|
'capslock': 20,
|
||||||
|
'esc': 27,
|
||||||
|
'space':32,
|
||||||
|
'spacebar':32,
|
||||||
|
' ':32,
|
||||||
|
'pageup': 33,
|
||||||
|
'pagedown': 34,
|
||||||
|
'end': 35,
|
||||||
|
'home': 36,
|
||||||
|
'leftarrow': 37,
|
||||||
|
'uparrow': 38,
|
||||||
|
'rightarrow': 39,
|
||||||
|
'downarrow': 40,
|
||||||
|
'insert': 45,
|
||||||
|
'delete': 46,
|
||||||
|
'0': 48,
|
||||||
|
'1': 49,
|
||||||
|
'2': 50,
|
||||||
|
'3': 51,
|
||||||
|
'4': 52,
|
||||||
|
'5': 53,
|
||||||
|
'6': 54,
|
||||||
|
'7': 55,
|
||||||
|
'8': 56,
|
||||||
|
'9': 57,
|
||||||
|
'a': 65,
|
||||||
|
'b': 66,
|
||||||
|
'c': 67,
|
||||||
|
'd': 68,
|
||||||
|
'e': 69,
|
||||||
|
'f': 70,
|
||||||
|
'g': 71,
|
||||||
|
'h': 72,
|
||||||
|
'i': 73,
|
||||||
|
'j': 74,
|
||||||
|
'k': 75,
|
||||||
|
'l': 76,
|
||||||
|
'm': 77,
|
||||||
|
'n': 78,
|
||||||
|
'o': 79,
|
||||||
|
'p': 80,
|
||||||
|
'q': 81,
|
||||||
|
'r': 82,
|
||||||
|
's': 83,
|
||||||
|
't': 84,
|
||||||
|
'u': 85,
|
||||||
|
'v': 86,
|
||||||
|
'w': 87,
|
||||||
|
'x': 88,
|
||||||
|
'y': 89,
|
||||||
|
'z': 90,
|
||||||
|
'A': 65,
|
||||||
|
'B': 66,
|
||||||
|
'C': 67,
|
||||||
|
'D': 68,
|
||||||
|
'E': 69,
|
||||||
|
'F': 70,
|
||||||
|
'G': 71,
|
||||||
|
'H': 72,
|
||||||
|
'I': 73,
|
||||||
|
'J': 74,
|
||||||
|
'K': 75,
|
||||||
|
'L': 76,
|
||||||
|
'M': 77,
|
||||||
|
'N': 78,
|
||||||
|
'O': 79,
|
||||||
|
'P': 80,
|
||||||
|
'Q': 81,
|
||||||
|
'R': 82,
|
||||||
|
'S': 83,
|
||||||
|
'T': 84,
|
||||||
|
'U': 85,
|
||||||
|
'V': 86,
|
||||||
|
'W': 87,
|
||||||
|
'X': 88,
|
||||||
|
'Y': 89,
|
||||||
|
'Z': 90,
|
||||||
|
'0numpad': 96,
|
||||||
|
'1numpad': 97,
|
||||||
|
'2numpad': 98,
|
||||||
|
'3numpad': 99,
|
||||||
|
'4numpad': 100,
|
||||||
|
'5numpad': 101,
|
||||||
|
'6numpad': 102,
|
||||||
|
'7numpad': 103,
|
||||||
|
'8numpad': 104,
|
||||||
|
'9numpad': 105,
|
||||||
|
'multiply': 106,
|
||||||
|
'plus': 107,
|
||||||
|
'minus': 109,
|
||||||
|
'decimal': 110,
|
||||||
|
'divide': 111,
|
||||||
|
'F1': 112,
|
||||||
|
'F2': 113,
|
||||||
|
'F3': 114,
|
||||||
|
'F4': 115,
|
||||||
|
'F5': 116,
|
||||||
|
'F6': 117,
|
||||||
|
'F7': 118,
|
||||||
|
'F8': 119,
|
||||||
|
'F9': 120,
|
||||||
|
'F10': 121,
|
||||||
|
'F11': 122,
|
||||||
|
'F12': 123,
|
||||||
|
'=': 187,
|
||||||
|
',': 188,
|
||||||
|
'.': 190,
|
||||||
|
'/': 191,
|
||||||
|
'`': 192,
|
||||||
|
'[': 219,
|
||||||
|
'\\': 220,
|
||||||
|
']': 221
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// These are public functions, intended to be used for developing plugins.
|
||||||
|
// They aren't considered part of the normal API for the core library.
|
||||||
|
//
|
||||||
|
|
||||||
|
module.normalizeTrialVariables = function(trial, protect) {
|
||||||
|
|
||||||
|
protect = (typeof protect === 'undefined') ? [] : protect;
|
||||||
|
|
||||||
|
var keys = getKeys(trial);
|
||||||
|
|
||||||
|
var tmp = {};
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
|
||||||
|
var process = true;
|
||||||
|
for (var j = 0; j < protect.length; j++) {
|
||||||
|
if (protect[j] == keys[i]) {
|
||||||
|
process = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof trial[keys[i]] == "function" && process) {
|
||||||
|
tmp[keys[i]] = trial[keys[i]].call();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tmp[keys[i]] = trial[keys[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// if possible_array is not an array, then return a one-element array
|
||||||
|
// containing possible_array
|
||||||
|
module.enforceArray = function(params, possible_arrays) {
|
||||||
|
|
||||||
|
// function to check if something is an array, fallback
|
||||||
|
// to string method if browser doesn't support Array.isArray
|
||||||
|
var ckArray = Array.isArray || function(a) {
|
||||||
|
return toString.call(a) == '[object Array]';
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < possible_arrays.length; i++) {
|
||||||
|
if(typeof params[possible_arrays[i]] !== 'undefined'){
|
||||||
|
params[possible_arrays[i]] = ckArray(params[possible_arrays[i]]) ? params[possible_arrays[i]] : [params[possible_arrays[i]]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
};
|
||||||
|
|
||||||
function getKeys(obj) {
|
function getKeys(obj) {
|
||||||
var r = [];
|
var r = [];
|
||||||
for (var k in obj) {
|
for (var k in obj) {
|
||||||
if (!obj.hasOwnProperty(k))
|
if (!obj.hasOwnProperty(k)) continue;
|
||||||
continue;
|
|
||||||
r.push(k);
|
r.push(k);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private function to flatten nested arrays
|
return module;
|
||||||
function flatten(arr, out) {
|
|
||||||
out = (typeof out === 'undefined') ? [] : out;
|
|
||||||
for (var i = 0; i < arr.length; i++) {
|
|
||||||
if (Array.isArray(arr[i])) {
|
|
||||||
flatten(arr[i], out);
|
|
||||||
} else {
|
|
||||||
out.push(arr[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
return core;
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// methods used in multiple modules
|
||||||
|
|
||||||
|
// private function to flatten nested arrays
|
||||||
|
function flatten(arr, out) {
|
||||||
|
out = (typeof out === 'undefined') ? [] : out;
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
if (Array.isArray(arr[i])) {
|
||||||
|
flatten(arr[i], out);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out.push(arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -1,33 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* jsPsych plugin for showing animations
|
* jsPsych plugin for showing animations and recording keyboard responses
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* updated January 2014
|
|
||||||
*
|
|
||||||
* shows a sequence of images at a fixed frame rate.
|
|
||||||
* subject can respond with keys if desired.
|
|
||||||
* entire animation sequence is recorded as JSON encoded string.
|
|
||||||
* responses are tagged with rt and image that was onscreen.
|
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* stimuli: array of arrays. inner arrays should consist of all the frames of the animation sequence. each inner array
|
|
||||||
* corresponds to a single trial
|
|
||||||
* frame_time: how long to display each frame in ms.
|
|
||||||
* frame_isi: length of gap between successive frames.
|
|
||||||
* repetitions: how many times to show the animation sequence.
|
|
||||||
* choices: array of valid key responses during animation.
|
|
||||||
* timing_post_trial: how long to show a blank screen after the trial in ms.
|
|
||||||
* prompt: optional HTML string to display while the animation is playing
|
|
||||||
* data: optional data object
|
|
||||||
*
|
*
|
||||||
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-animation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
jsPsych.animation = (function() {
|
jsPsych.animation = (function() {
|
||||||
|
|
||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['choices', 'data']);
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
@ -50,7 +35,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
var interval_time = trial.frame_time + trial.frame_isi;
|
var interval_time = trial.frame_time + trial.frame_isi;
|
||||||
var animate_frame = -1;
|
var animate_frame = -1;
|
||||||
@ -109,37 +94,28 @@
|
|||||||
}, trial.frame_time);
|
}, trial.frame_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp_func = function(e) {
|
|
||||||
var flag = false;
|
|
||||||
// check if the key is any of the options, or if it is an accidental keystroke
|
|
||||||
for (var i = 0; i < trial.choices.length; i++) {
|
|
||||||
if (e.which == trial.choices[i]) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag) {
|
|
||||||
var key_press = e.which;
|
|
||||||
|
|
||||||
// record rt
|
|
||||||
var endTime = (new Date()).getTime();
|
|
||||||
|
|
||||||
responses.push({
|
|
||||||
"key_press": key_press,
|
|
||||||
"rt": endTime - startTime,
|
|
||||||
"stimulus": current_stim
|
|
||||||
});
|
|
||||||
|
|
||||||
// after a valid response, the stimulus will have the CSS class 'responded'
|
|
||||||
// which can be used to provide visual feedback that a response was recorded
|
|
||||||
$("#jspsych-animation-image").addClass('responded');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).keydown(resp_func);
|
var after_response = function(info) {
|
||||||
|
|
||||||
|
responses.push({
|
||||||
|
key_press: info.key,
|
||||||
|
rt: info.rt,
|
||||||
|
stimulus: current_stim
|
||||||
|
});
|
||||||
|
|
||||||
|
// after a valid response, the stimulus will have the CSS class 'responded'
|
||||||
|
// which can be used to provide visual feedback that a response was recorded
|
||||||
|
$("#jspsych-animation-image").addClass('responded');
|
||||||
|
}
|
||||||
|
|
||||||
|
// hold the jspsych response listener object in memory
|
||||||
|
// so that we can turn off the response collection when
|
||||||
|
// the trial ends
|
||||||
|
var response_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', true);
|
||||||
|
|
||||||
function endTrial() {
|
function endTrial() {
|
||||||
$(document).unbind('keydown', resp_func);
|
|
||||||
|
jsPsych.pluginAPI.cancelKeyboardResponse(response_listener);
|
||||||
|
|
||||||
block.writeData($.extend({}, {
|
block.writeData($.extend({}, {
|
||||||
"trial_type": "animation",
|
"trial_type": "animation",
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
|
* jspsych-call-function
|
||||||
|
* plugin for calling an arbitrary function during a jspsych experiment
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* Updated October 2013
|
*
|
||||||
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-call-function
|
||||||
This plugin gives the user the ability to execute an arbitrary function
|
*
|
||||||
during an experiment.
|
|
||||||
|
|
||||||
Params:
|
|
||||||
"type" is "call_function"
|
|
||||||
"func" is the function that will be called
|
|
||||||
"args" is an array of arguments to pass to the function. (optional)
|
|
||||||
|
|
||||||
Data:
|
|
||||||
The return value of the function will be stored in the data.
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -45,4 +38,4 @@
|
|||||||
|
|
||||||
return plugin;
|
return plugin;
|
||||||
})();
|
})();
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -1,27 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* jspsych plugin for categorization trials with feedback and animated stimuli
|
* jspsych plugin for categorization trials with feedback and animated stimuli
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* updated December 2013
|
|
||||||
*
|
*
|
||||||
* display a sequence of images at a fixed frame rate then give corrective feedback based on the subject's response
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-categorize-animation
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* stimuli: array of arrays. inner array elements are paths to images. each inner array is a set of frames
|
|
||||||
* that will be displayed as an animation sequence.
|
|
||||||
* key_answer: array of key codes representing the correct answer for each stimulus.
|
|
||||||
* text_answer: array of strings representing the label associated with each stimulus. optional.
|
|
||||||
* choices: array of key codes representing valid choices that can be made. other key responses will be ignored.
|
|
||||||
* correct_text: HTML string to show when correct answer is given.
|
|
||||||
* incorrect_text: HTML string to show when incorrect answer is given.
|
|
||||||
* NOTE: for both of the above, the special string %ANS% can be used. The text_answer associated with
|
|
||||||
* the trial will be substituted for %ANS%.
|
|
||||||
* reps: how many cycles through the animation sequence to show. -1 will show until response is given.
|
|
||||||
* allow_response_before_complete: if true, the subject can give a response before the animation sequence finishes.
|
|
||||||
* timing_feedback_duration: how long to show the feedback for.
|
|
||||||
* timing_post_trial: how long to show a blank screen before the next trial.
|
|
||||||
* frame_time: how many ms to show each individual image in the animation sequence.
|
|
||||||
* prompt: HTML string to show when the subject is viewing the stimulus and making a categorization decision.
|
|
||||||
* data: the optional data object
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -30,6 +11,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['key_answer','text_answer','choices','data']);
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
@ -56,7 +40,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
var animate_frame = -1;
|
var animate_frame = -1;
|
||||||
var reps = 0;
|
var reps = 0;
|
||||||
@ -86,7 +70,7 @@
|
|||||||
if (showAnimation) {
|
if (showAnimation) {
|
||||||
display_element.append($('<img>', {
|
display_element.append($('<img>', {
|
||||||
"src": trial.stims[animate_frame],
|
"src": trial.stims[animate_frame],
|
||||||
"class": 'jspsych-categorize-animate-stimulus'
|
"class": 'jspsych-categorize-animation-stimulus'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,50 +114,39 @@
|
|||||||
|
|
||||||
}, trial.frame_time);
|
}, trial.frame_time);
|
||||||
|
|
||||||
// attach response function
|
|
||||||
|
var keyboard_listener;
|
||||||
var resp_func = function(e) {
|
|
||||||
|
var after_response = function(info){
|
||||||
|
// ignore the response if animation is playing and subject
|
||||||
|
// not allowed to respond before it is complete
|
||||||
if (!trial.allow_response_before_complete && showAnimation) {
|
if (!trial.allow_response_before_complete && showAnimation) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flag = false; // valid keystroke?
|
var correct = false;
|
||||||
var correct = false; // correct answer?
|
if(trial.key_answer == info.key) {
|
||||||
|
|
||||||
if (e.which == trial.key_answer) // correct category
|
|
||||||
{
|
|
||||||
flag = true;
|
|
||||||
correct = true;
|
correct = true;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// check if the key is any of the options, or if it is an accidental keystroke
|
responded = true;
|
||||||
for (var i = 0; i < trial.choices.length; i++) {
|
|
||||||
if (e.which == trial.choices[i]) {
|
|
||||||
flag = true;
|
|
||||||
correct = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag) // if keystroke is one of the choices
|
|
||||||
{
|
|
||||||
responded = true;
|
|
||||||
var endTime = (new Date()).getTime();
|
|
||||||
var rt = (endTime - startTime);
|
|
||||||
|
|
||||||
var trial_data = {
|
var trial_data = {
|
||||||
"trial_type": trial.type,
|
"trial_type": trial.type,
|
||||||
"trial_index": block.trial_idx,
|
"trial_index": block.trial_idx,
|
||||||
"stimulus": trial.stims[0],
|
"stimulus": trial.stims[0],
|
||||||
"rt": rt,
|
"rt": info.rt,
|
||||||
"correct": correct,
|
"correct": correct,
|
||||||
"key_press": e.which
|
"key_press": info.key
|
||||||
};
|
};
|
||||||
block.writeData($.extend({}, trial_data, trial.data));
|
|
||||||
$(document).unbind('keydown', resp_func);
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
}
|
|
||||||
};
|
jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
|
||||||
$(document).keydown(resp_func);
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', true);
|
||||||
|
|
||||||
function endTrial() {
|
function endTrial() {
|
||||||
clearInterval(animate_interval); // stop animation!
|
clearInterval(animate_interval); // stop animation!
|
||||||
|
@ -1,27 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* jspsych plugin for categorization trials with feedback
|
* jspsych plugin for categorization trials with feedback
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* updated October 2013
|
|
||||||
*
|
*
|
||||||
* display an image or HTML object and then give corrective feedback based on the subject's response
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-categorize
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* stimuli: array of stimuli. array elements can be paths to images or strings of HTML.
|
|
||||||
* key_answer: array of key codes representing the correct answer for each stimulus.
|
|
||||||
* text_answer: array of strings representing the label associated with each stimulus. optional.
|
|
||||||
* choices: array of key codes representing valid choices that can be made. other key responses will be ignored.
|
|
||||||
* correct_text: HTML string to show when correct answer is given.
|
|
||||||
* incorrect_text: HTML string to show when incorrect answer is given.
|
|
||||||
* NOTE: for both of the above, the special string %ANS% can be used. The text_answer associated with
|
|
||||||
* the trial will be substituted for %ANS%.
|
|
||||||
* timing_stim: how long to show the stimulus for. -1 will show until response is given.
|
|
||||||
* timing_feedback_duration: how long to show the feedback for.
|
|
||||||
* timing_post_trial: how long to show a blank screen before the next trial.
|
|
||||||
* show_stim_with_feedback: if true, the stimulus will remain on the screen while feedback is given.
|
|
||||||
* is_html: must set to true if the stimulus is HTML code.
|
|
||||||
* force_correct_button_press: if true, then the user must press the correct key after feedback is given.
|
|
||||||
* prompt: HTML string to show when the subject is viewing the stimulus and making a categorization decision.
|
|
||||||
* data: the optional data object
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -30,6 +11,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['choices', 'stimuli', 'key_answer', 'text_answer', 'data']);
|
||||||
|
|
||||||
var trials = [];
|
var trials = [];
|
||||||
for (var i = 0; i < params.stimuli.length; i++) {
|
for (var i = 0; i < params.stimuli.length; i++) {
|
||||||
trials.push({});
|
trials.push({});
|
||||||
@ -61,7 +45,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -78,9 +62,9 @@
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
display_element.append($('<div>', {
|
display_element.append($('<div>', {
|
||||||
id: 'jspsych-categorize-stimulus',
|
"id": 'jspsych-categorize-stimulus',
|
||||||
"class": 'cat',
|
"class": 'jspsych-categorize-stimulus',
|
||||||
html: trial.a_path
|
"html": trial.a_path
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,51 +86,32 @@
|
|||||||
var startTime = (new Date()).getTime();
|
var startTime = (new Date()).getTime();
|
||||||
|
|
||||||
// create response function
|
// create response function
|
||||||
var resp_func = function(e) {
|
var after_response = function(info) {
|
||||||
var flag = false;
|
|
||||||
var correct = false;
|
var correct = false;
|
||||||
if (e.which == trial.key_answer) // correct category
|
if(trial.key_answer == info.key) { correct = true; }
|
||||||
{
|
|
||||||
flag = true;
|
cat_trial_complete = true;
|
||||||
correct = true;
|
|
||||||
}
|
// save data
|
||||||
else {
|
var trial_data = {
|
||||||
// check if the key is any of the options, or if it is an accidental keystroke
|
"trial_type": "categorize",
|
||||||
for (var i = 0; i < trial.choices.length; i++) {
|
"trial_index": block.trial_idx,
|
||||||
if (e.which == trial.choices[i]) {
|
"rt": info.rt,
|
||||||
flag = true;
|
"correct": correct,
|
||||||
correct = false;
|
"stimulus": trial.a_path,
|
||||||
}
|
"key_press": info.key
|
||||||
}
|
};
|
||||||
}
|
|
||||||
if (flag) {
|
|
||||||
cat_trial_complete = true;
|
|
||||||
|
|
||||||
// measure response time
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
var endTime = (new Date()).getTime();
|
|
||||||
var rt = (endTime - startTime);
|
|
||||||
|
|
||||||
// save data
|
display_element.html('');
|
||||||
var trial_data = {
|
|
||||||
"trial_type": "categorize",
|
plugin.trial(display_element, block, trial, part + 1);
|
||||||
"trial_index": block.trial_idx,
|
}
|
||||||
"rt": rt,
|
|
||||||
"correct": correct,
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', false);
|
||||||
"stimulus": trial.a_path,
|
|
||||||
"key_press": e.which
|
|
||||||
};
|
|
||||||
|
|
||||||
block.writeData($.extend({}, trial_data, trial.data));
|
|
||||||
|
|
||||||
// clear function
|
|
||||||
$(document).unbind('keydown', resp_func);
|
|
||||||
display_element.html('');
|
|
||||||
plugin.trial(display_element, block, trial, part + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// add event listener
|
|
||||||
$(document).keydown(resp_func);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
@ -163,8 +128,8 @@
|
|||||||
else {
|
else {
|
||||||
display_element.append($('<div>', {
|
display_element.append($('<div>', {
|
||||||
"id": 'jspsych-categorize-stimulus',
|
"id": 'jspsych-categorize-stimulus',
|
||||||
"class": 'cat',
|
"class": 'jspsych-categorize-stimulus',
|
||||||
"html": trial.a_path
|
"html: trial.a_path
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,14 +148,13 @@
|
|||||||
|
|
||||||
// check if force correct button press is set
|
// check if force correct button press is set
|
||||||
if (trial.force_correct_button_press && block.data[block.trial_idx].correct === false) {
|
if (trial.force_correct_button_press && block.data[block.trial_idx].correct === false) {
|
||||||
var resp_func_corr_key = function(e) {
|
|
||||||
if (e.which == trial.key_answer) // correct category
|
var after_forced_response = function(info) {
|
||||||
{
|
plugin.trial(display_element, block, trial, part + 1);
|
||||||
$(document).unbind('keyup', resp_func_corr_key);
|
}
|
||||||
plugin.trial(display_element, block, trial, part + 1);
|
|
||||||
}
|
jsPsych.pluginAPI.getKeyboardResponse(after_forced_response, trial.key_answer, 'date', false);
|
||||||
};
|
|
||||||
$(document).keyup(resp_func_corr_key);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
|
* jspsych-free-sort
|
||||||
|
* plugin for drag-and-drop sorting of a collection of images
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* Updated October 2013
|
|
||||||
*
|
|
||||||
* This plugin displays a set of images on the screen and allows the user to drag them around.
|
|
||||||
* The location of each object and the moves that the subject performs are recorded.
|
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* stimuli: array of arrays. inner most arrays are collections of image paths that will be displayed in a trial. outer
|
|
||||||
* arrays are trials.
|
|
||||||
* stim_height: the height of the images to sort in pixels.
|
|
||||||
* stim_width: the width of the images to sort in pixels.
|
|
||||||
* timing_post_trial: how long to show a blank screen after the trial in ms.
|
|
||||||
* prompt: optional html string to display while sorting happens.
|
|
||||||
* prompt_location: either 'above' or 'below'; changes location of prompt
|
|
||||||
* sort_area_width: width of the area used to sort the images
|
|
||||||
* sort_area_height: height of the area used to sort the images
|
|
||||||
* data: optional data object
|
|
||||||
*
|
*
|
||||||
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-free-sort
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -26,6 +13,8 @@
|
|||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {
|
trials[i] = {
|
||||||
@ -49,7 +38,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
var start_time = (new Date()).getTime();
|
var start_time = (new Date()).getTime();
|
||||||
|
|
||||||
|
@ -3,32 +3,7 @@ The html-plugin will load and display an arbitrary number of html pages. To proc
|
|||||||
user might either press a button on the page or a specific key. Afterwards, the page get hidden and
|
user might either press a button on the page or a specific key. Afterwards, the page get hidden and
|
||||||
the plugin will wait of a specified time before it proceeds.
|
the plugin will wait of a specified time before it proceeds.
|
||||||
|
|
||||||
Parameters:
|
documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-html
|
||||||
pages: array of
|
|
||||||
url: url of the html-page to display (mandatory)
|
|
||||||
cont_key: keycode of the key to continue (optional)
|
|
||||||
cont_btn: id of a button (or any element on the page) that can be clicked to continue (optional)
|
|
||||||
note that the button / element has to be included in the page that is loaded, already
|
|
||||||
timing: number of ms to wait after hiding the page and before proceeding (optional)
|
|
||||||
check_fn: called with display_element as argument when subject attempts to proceed; only proceeds if this
|
|
||||||
returns true; (optional)
|
|
||||||
cont_key: this setting is used for all pages that don't define it themself (optional)
|
|
||||||
cont_btn: this setting is used for all pages that don't define it themself (optional)
|
|
||||||
timing: this setting is used for all pages that don't define it themself (optional)
|
|
||||||
force_refresh: set to true if you want to force the plugin to grab the newest version of the html document
|
|
||||||
|
|
||||||
Data:
|
|
||||||
array of
|
|
||||||
url: the url of the page
|
|
||||||
rt: duration the user looked at the page in ms
|
|
||||||
|
|
||||||
|
|
||||||
Example Usage:
|
|
||||||
jsPsych.init(
|
|
||||||
{experiment_structure: [
|
|
||||||
{type: "html", pages:[{url: "intro.html", cont_btn: "start"}]}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
*/
|
*/
|
||||||
(function($) {
|
(function($) {
|
||||||
jsPsych.html = (function() {
|
jsPsych.html = (function() {
|
||||||
@ -36,7 +11,12 @@ Example Usage:
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['pages']);
|
||||||
|
|
||||||
var trials = [];
|
var trials = [];
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < params.pages.length; i++) {
|
for (var i = 0; i < params.pages.length; i++) {
|
||||||
trials.push({
|
trials.push({
|
||||||
type: "html",
|
type: "html",
|
||||||
@ -56,7 +36,7 @@ Example Usage:
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial, ["check_fn"]);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial, ["check_fn"]);
|
||||||
|
|
||||||
var url = trial.url;
|
var url = trial.url;
|
||||||
if (trial.force_refresh) {
|
if (trial.force_refresh) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/** jspsych-palmer
|
/**
|
||||||
|
* jspsych-palmer
|
||||||
* Josh de Leeuw (October 2013)
|
* Josh de Leeuw (October 2013)
|
||||||
*
|
*
|
||||||
* a jspsych plugin for presenting and querying about stimuli modeled after
|
* a jspsych plugin for presenting and querying about stimuli modeled after
|
||||||
@ -10,21 +11,7 @@
|
|||||||
* Goldstone, R. L., Rogosky, B. J., Pevtzow, R., & Blair, M. (2005). Perceptual and semantic reorganization during category learning.
|
* Goldstone, R. L., Rogosky, B. J., Pevtzow, R., & Blair, M. (2005). Perceptual and semantic reorganization during category learning.
|
||||||
* In H. Cohen & C. Lefebvre (Eds.) Handbook of Categorization in Cognitive Science. (pp. 651-678). Amsterdam: Elsevier.
|
* In H. Cohen & C. Lefebvre (Eds.) Handbook of Categorization in Cognitive Science. (pp. 651-678). Amsterdam: Elsevier.
|
||||||
*
|
*
|
||||||
* NOTE: This plugin requires the Raphaeljs library for manipulating vector graphics (SVG). Download at http://www.raphaeljs.com
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-palmer
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* configurations: array of arrays. inner most array should be an array of 1s and 0s, where 1s represent the
|
|
||||||
* presence of a line segment, and 0s represent the absence.
|
|
||||||
* editable: set to true if you want the subject to be able to change the configuration by interacting
|
|
||||||
* with the stimulus. (click two circles to toggle the line between them).
|
|
||||||
* show_feedback: set to true to show corrective feedback when trial is editable.
|
|
||||||
* grid_spacing: distance in pixels between the circles.
|
|
||||||
* square_Size: how many circles per row/column.
|
|
||||||
* timing_item: how long to show the stimulus for. (only matters when editable is false)
|
|
||||||
* timing_post_trial: how long to show blank screen after trial.
|
|
||||||
* timing_feedback: how long to show corrective feedback for.
|
|
||||||
* prompt: optional html string to show during stimulus presentation
|
|
||||||
* data: optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -34,6 +21,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
var trials = [];
|
var trials = [];
|
||||||
for (var i = 0; i < params.configurations.length; i++) {
|
for (var i = 0; i < params.configurations.length; i++) {
|
||||||
var trial = {
|
var trial = {
|
||||||
@ -61,7 +51,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
// variables to keep track of user interaction
|
// variables to keep track of user interaction
|
||||||
var start_circle = -1;
|
var start_circle = -1;
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* jspsych-same-different
|
* jspsych-same-different
|
||||||
* Josh de Leeuw (Updated Oct 2013)
|
* Josh de Leeuw
|
||||||
*
|
*
|
||||||
* plugin for showing two stimuli sequentially and getting a same / different judgment
|
* plugin for showing two stimuli sequentially and getting a same / different judgment
|
||||||
*
|
*
|
||||||
* parameters:
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-same-different
|
||||||
* stimuli: array of arrays. inner most array should have two elements, corresponding to the two items that will be shown.
|
|
||||||
* items can be image paths or HTML strings. each inner array is a trial.
|
|
||||||
* answer: array of strings. acceptable values are "same" and "different". represents the correct answer for each trial.
|
|
||||||
* same_key: which key to press to indicate a 'same' response.
|
|
||||||
* different_key: which key to press to indicate a 'different' response.
|
|
||||||
* timing_first_stim: how long to show the first stimulus
|
|
||||||
* timing_second_stim: how long to show the second stim. can be -1, which means to show until a response is given.
|
|
||||||
* timing_gap: how long to show a blank screen in between the two stimuli.
|
|
||||||
* timing_post_trial: how long to show a blank screen after the trial ends.
|
|
||||||
* is_html: must set to true if the stimulus is HTML code.
|
|
||||||
* prompt: HTML string to show when the subject is viewing the stimulus and making a categorization decision.
|
|
||||||
* data: the optional data object
|
|
||||||
*
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -26,6 +13,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data','answer'])
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
@ -55,7 +45,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
|
|
||||||
switch (part) {
|
switch (part) {
|
||||||
@ -113,50 +103,42 @@
|
|||||||
display_element.append(trial.prompt);
|
display_element.append(trial.prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTime = (new Date()).getTime();
|
var after_response = function(info){
|
||||||
|
|
||||||
var resp_func = function(e) {
|
|
||||||
var flag = false;
|
|
||||||
var correct = false;
|
var correct = false;
|
||||||
if (e.which == trial.same_key) {
|
|
||||||
flag = true;
|
if(info.key == trial.same_key && trial.answer == 'same'){
|
||||||
if (trial.answer == "same") {
|
correct = true;
|
||||||
correct = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (e.which == trial.different_key) {
|
|
||||||
flag = true;
|
if(info.key == trial.different_key && trial.answer == 'different'){
|
||||||
if (trial.answer == "different") {
|
correct = true;
|
||||||
correct = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (flag) {
|
|
||||||
var endTime = (new Date()).getTime();
|
var trial_data = {
|
||||||
var rt = (endTime - startTime);
|
"trial_type": "same-different",
|
||||||
|
"trial_index": block.trial_idx,
|
||||||
var trial_data = {
|
"rt": info.rt,
|
||||||
"trial_type": "same-different",
|
"correct": correct,
|
||||||
"trial_index": block.trial_idx,
|
"stimulus": trial.a_path,
|
||||||
"rt": rt,
|
"stimulus_2": trial.b_path,
|
||||||
"correct": correct,
|
"key_press": info.key
|
||||||
"stimulus": trial.a_path,
|
};
|
||||||
"stimulus_2": trial.b_path,
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
"key_press": e.which
|
|
||||||
};
|
display_element.html('');
|
||||||
block.data[block.trial_idx] = $.extend({}, trial_data, trial.data);
|
|
||||||
$(document).unbind('keydown', resp_func);
|
if(trial.timing_post_trial > 0) {
|
||||||
|
setTimeout(function() {
|
||||||
display_element.html('');
|
|
||||||
if(trial.timing_post_trial > 0) {
|
|
||||||
setTimeout(function() {
|
|
||||||
block.next();
|
|
||||||
}, trial.timing_post_trial);
|
|
||||||
} else {
|
|
||||||
block.next();
|
block.next();
|
||||||
}
|
}, trial.timing_post_trial);
|
||||||
|
} else {
|
||||||
|
block.next();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
$(document).keydown(resp_func);
|
|
||||||
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.same_key, trial.different_key], 'date', false);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,19 +4,7 @@
|
|||||||
*
|
*
|
||||||
* This plugin create a trial where two images are shown sequentially, and the subject rates their similarity using a slider controlled with the mouse.
|
* This plugin create a trial where two images are shown sequentially, and the subject rates their similarity using a slider controlled with the mouse.
|
||||||
*
|
*
|
||||||
* parameters:
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-similarity
|
||||||
* stimuli: array of arrays. inner arrays are two stimuli. stimuli can be image paths or html strings. each inner array is one trial.
|
|
||||||
* labels: array of strings to label the slider with. labels will be evenly spaced along the slider.
|
|
||||||
* intervals: how many different response options are on the slider
|
|
||||||
* show_ticks: if true, then the slider will have small tick marks displayed to show where the response options are.
|
|
||||||
* show_response: determines when to show the response options: "FIRST_STIMULUS","SECOND_STIMULUS",or "POST_STIMULUS"
|
|
||||||
* timing_first_stim: how long to show the first stimulus.
|
|
||||||
* timing_second_stim: how long to show the second stimulus. can be -1 to show until a response is given.
|
|
||||||
* timing_image_gap: how long to show a blank screen between the two stimuli.
|
|
||||||
* timing_post_trial: how long to show a blank screen after the trial is over.
|
|
||||||
* is_html: must set to true when using HTML strings as the stimuli.
|
|
||||||
* prompt: optional HTML string to display with the stimulus.
|
|
||||||
* data: the optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -26,14 +14,15 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
trials[i].type = "similarity";
|
trials[i].type = "similarity";
|
||||||
trials[i].a_path = params.stimuli[i][0];
|
trials[i].a_path = params.stimuli[i][0];
|
||||||
trials[i].b_path = params.stimuli[i][1];
|
trials[i].b_path = params.stimuli[i][1];
|
||||||
|
|
||||||
// TODO make all changes related to following 4 parameters.
|
|
||||||
trials[i].labels = (typeof params.labels === 'undefined') ? ["Not at all similar", "Identical"] : params.labels;
|
trials[i].labels = (typeof params.labels === 'undefined') ? ["Not at all similar", "Identical"] : params.labels;
|
||||||
trials[i].intervals = params.intervals || 100;
|
trials[i].intervals = params.intervals || 100;
|
||||||
trials[i].show_ticks = (typeof params.show_ticks === 'undefined') ? false : params.show_ticks;
|
trials[i].show_ticks = (typeof params.show_ticks === 'undefined') ? false : params.show_ticks;
|
||||||
@ -59,7 +48,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1,25 +1,12 @@
|
|||||||
// Josh de Leeuw
|
/**
|
||||||
// Updated October 2013
|
* jspsych-single-stim
|
||||||
//
|
* Josh de Leeuw
|
||||||
// This plugin is for presenting a single image and collecting a key response.
|
*
|
||||||
// It can be used for categorizing images (without feedback), collecting yes/no responses, etc...
|
* plugin for displaying a stimulus and getting a keyboard response
|
||||||
//
|
*
|
||||||
// parameters
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-single-stim
|
||||||
// stimuli: array of stimuli to present. elements of the array can be either paths to images
|
*
|
||||||
// or HTML strings
|
**/
|
||||||
// choices: array of key codes that represent valid responses. other key codes will be ignored
|
|
||||||
// continue_after_response: when true, the trial will end as soon as the user gives a response.
|
|
||||||
// if false, then the trial will continue until timing_response is reached
|
|
||||||
// timing_stim: how long to show the stimulus for. -1 will show indefinitely.
|
|
||||||
// timing_response: how long to wait for a response. this timer starts at the same time as the
|
|
||||||
// timer for the stimulus presentation. if the timer is reached without a response
|
|
||||||
// given, then the user's response will be recorded as a "-1" for the trial, and the
|
|
||||||
// trial will end.
|
|
||||||
// timing_post_trial: how long to show a blank screen after the trial ends.
|
|
||||||
// is_html: must set to true when using HTML strings as the stimuli.
|
|
||||||
// prompt: optional HTML string to display with the stimulus.
|
|
||||||
// data: the optional data object
|
|
||||||
|
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
jsPsych["single-stim"] = (function() {
|
jsPsych["single-stim"] = (function() {
|
||||||
@ -27,6 +14,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['stimuli', 'choices', 'data']);
|
||||||
|
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
@ -56,15 +46,10 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
var trial_complete = false;
|
var trial_complete = false;
|
||||||
|
|
||||||
var startTime = (new Date()).getTime();
|
|
||||||
var endTime = -1;
|
|
||||||
|
|
||||||
var key_press = -1;
|
|
||||||
|
|
||||||
if (!trial.is_html) {
|
if (!trial.is_html) {
|
||||||
display_element.append($('<img>', {
|
display_element.append($('<img>', {
|
||||||
src: trial.a_path,
|
src: trial.a_path,
|
||||||
@ -83,23 +68,20 @@
|
|||||||
display_element.append(trial.prompt);
|
display_element.append(trial.prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cont_function = function() {
|
var end_trial = function(info) {
|
||||||
var rt = -1;
|
|
||||||
if (endTime != -1) {
|
|
||||||
rt = (endTime - startTime);
|
|
||||||
}
|
|
||||||
trial_complete = true;
|
trial_complete = true;
|
||||||
|
|
||||||
var trial_data = {
|
var trial_data = {
|
||||||
"trial_type": "single-stim",
|
"trial_type": "single-stim",
|
||||||
"trial_index": block.trial_idx,
|
"trial_index": block.trial_idx,
|
||||||
"rt": rt,
|
"rt": info.rt,
|
||||||
"stimulus": trial.a_path,
|
"stimulus": trial.a_path,
|
||||||
"key_press": key_press
|
"key_press": info.key
|
||||||
};
|
};
|
||||||
|
|
||||||
block.writeData($.extend({}, trial_data, trial.data));
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
$(document).unbind('keydown', resp_func);
|
|
||||||
display_element.html('');
|
display_element.html('');
|
||||||
if (trial.timing_post_trial > 0) {
|
if (trial.timing_post_trial > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -111,35 +93,23 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var resp_func = function(e) {
|
var after_response = function(info) {
|
||||||
var flag = false;
|
|
||||||
// check if the key is any of the options, or if it is an accidental keystroke
|
// after a valid response, the stimulus will have the CSS class 'responded'
|
||||||
for (var i = 0; i < trial.choices.length; i++) {
|
// which can be used to provide visual feedback that a response was recorded
|
||||||
if (e.which == trial.choices[i]) {
|
$("#jspsych-single-stim-stimulus").addClass('responded');
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag) {
|
|
||||||
key_press = e.which;
|
|
||||||
|
|
||||||
// record rt
|
if (trial.continue_after_response) {
|
||||||
endTime = (new Date()).getTime();
|
// response triggers the next trial in this case.
|
||||||
|
// if hide_image_after_response is true, then next
|
||||||
// after a valid response, the stimulus will have the CSS class 'responded'
|
// trial should be triggered by timeout function below.
|
||||||
// which can be used to provide visual feedback that a response was recorded
|
end_trial(info);
|
||||||
$("#jspsych-single-stim-stimulus").addClass('responded');
|
|
||||||
|
|
||||||
if (trial.continue_after_response) {
|
|
||||||
// response triggers the next trial in this case.
|
|
||||||
// if hide_image_after_response is true, then next
|
|
||||||
// trial should be triggered by timeout function below.
|
|
||||||
cont_function();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$(document).keydown(resp_func);
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices);
|
||||||
|
|
||||||
// hide image if timing is set
|
// hide image if timing is set
|
||||||
if (trial.timing_stim > 0) {
|
if (trial.timing_stim > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -153,7 +123,7 @@
|
|||||||
if (trial.timing_response > 0) {
|
if (trial.timing_response > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (!trial_complete) {
|
if (!trial_complete) {
|
||||||
cont_function();
|
end_trial({rt: -1, key: -1});
|
||||||
}
|
}
|
||||||
}, trial.timing_response);
|
}, trial.timing_response);
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,9 @@
|
|||||||
* jspsych-survey-likert
|
* jspsych-survey-likert
|
||||||
* a jspsych plugin for measuring items on a likert scale
|
* a jspsych plugin for measuring items on a likert scale
|
||||||
*
|
*
|
||||||
* Josh de Leeuw (March 2013)
|
* Josh de Leeuw
|
||||||
* Updated October 2013
|
|
||||||
*
|
*
|
||||||
* parameters:
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-survey-likert
|
||||||
* questions: array of arrays. inner arrays are arrays of strings, where each string represents a prompt
|
|
||||||
* for the user to respond to.
|
|
||||||
* labels: array of arrays of arrays. inner most arrays are label markers for the slider, e.g. ["Strongly Disagree", "Neutral", "Strongly Agree"].
|
|
||||||
* need one inner array for every question that is part of the trial. middle arrays group questions together.
|
|
||||||
* intervals: array of arrays. inner arrays are how many different responses the user can select from, e.g. 5, one for each question.
|
|
||||||
* show_ticks: graphically show tick marks on the slider bar to indicate the response levels.
|
|
||||||
* data: optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -22,6 +14,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
var trials = [];
|
var trials = [];
|
||||||
for (var i = 0; i < params.questions.length; i++) {
|
for (var i = 0; i < params.questions.length; i++) {
|
||||||
trials.push({
|
trials.push({
|
||||||
@ -41,7 +36,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
// add likert scale questions
|
// add likert scale questions
|
||||||
for (var i = 0; i < trial.questions.length; i++) {
|
for (var i = 0; i < trial.questions.length; i++) {
|
||||||
|
@ -2,13 +2,9 @@
|
|||||||
* jspsych-survey-text
|
* jspsych-survey-text
|
||||||
* a jspsych plugin for free response survey questions
|
* a jspsych plugin for free response survey questions
|
||||||
*
|
*
|
||||||
* Josh de Leeuw (March 2013)
|
* Josh de Leeuw
|
||||||
* Updated October 2013
|
|
||||||
*
|
*
|
||||||
* parameters:
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-survey-text
|
||||||
* questions: array of arrays. inner arrays are arrays of strings, where each string represents a prompt
|
|
||||||
* for the user to respond to.
|
|
||||||
* data: optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,6 +14,9 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
var trials = [];
|
var trials = [];
|
||||||
for (var i = 0; i < params.questions.length; i++) {
|
for (var i = 0; i < params.questions.length; i++) {
|
||||||
trials.push({
|
trials.push({
|
||||||
@ -34,7 +33,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
// add likert scale questions
|
// add likert scale questions
|
||||||
for (var i = 0; i < trial.questions.length; i++) {
|
for (var i = 0; i < trial.questions.length; i++) {
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* This plugin displays text (including HTML formatted strings) during the experiment.
|
* This plugin displays text (including HTML formatted strings) during the experiment.
|
||||||
* Use it to show instructions, provide performance feedback, etc...
|
* Use it to show instructions, provide performance feedback, etc...
|
||||||
|
*
|
||||||
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-text
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -14,14 +16,14 @@
|
|||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
params = jsPsych.enforceArray(params, ['text','data']);
|
params = jsPsych.pluginAPI.enforceArray(params, ['text','data']);
|
||||||
|
|
||||||
var trials = new Array(params.text.length);
|
var trials = new Array(params.text.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
trials[i].type = "text"; // must match plugin name
|
trials[i].type = "text"; // must match plugin name
|
||||||
trials[i].text = params.text[i]; // text of all trials
|
trials[i].text = params.text[i]; // text of all trials
|
||||||
trials[i].cont_key = params.cont_key || '13'; // keycode to press to advance screen, default is ENTER.
|
trials[i].cont_key = params.cont_key || []; // keycode to press to advance screen, default is all keys.
|
||||||
trials[i].timing_post_trial = (typeof params.timing_post_trial === 'undefined') ? 0 : params.timing_post_trial;
|
trials[i].timing_post_trial = (typeof params.timing_post_trial === 'undefined') ? 0 : params.timing_post_trial;
|
||||||
trials[i].data = (typeof params.data === 'undefined') ? {} : params.data[i];
|
trials[i].data = (typeof params.data === 'undefined') ? {} : params.data[i];
|
||||||
}
|
}
|
||||||
@ -33,48 +35,17 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
// set the HTML of the display target to replaced_text.
|
// set the HTML of the display target to replaced_text.
|
||||||
display_element.html(trial.text);
|
display_element.html(trial.text);
|
||||||
|
|
||||||
var startTime = (new Date()).getTime();
|
var after_response = function(info) {
|
||||||
|
|
||||||
// it's possible that if the user is holding down the cont_key when
|
|
||||||
// they arrive on the page that they will advance as soon as the
|
|
||||||
// key is released. this prevents that from happening by requiring a
|
|
||||||
// full cycle on the page with a down and up event.
|
|
||||||
var cont_key_down = false;
|
|
||||||
|
|
||||||
// define a function that will advance to the next trial when the user presses
|
|
||||||
// the continue key.
|
|
||||||
var key_listener = function(e) {
|
|
||||||
if (e.which == trial.cont_key && cont_key_down) {
|
|
||||||
save_data();
|
|
||||||
$(document).unbind('keyup', key_listener); // remove the response function, so that it doesn't get triggered again.
|
|
||||||
$(document).unbind('keydown', key_down_listener);
|
|
||||||
display_element.html(''); // clear the display
|
|
||||||
if (trial.timing_post_trial > 0) {
|
|
||||||
setTimeout(function() {
|
|
||||||
block.next();
|
|
||||||
}, trial.timing_post_trial);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
block.next();
|
|
||||||
} // call block.next() to advance the experiment after a delay.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var key_down_listener = function(e) {
|
|
||||||
if (e.which == trial.cont_key) {
|
|
||||||
cont_key_down = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mouse_listener = function(e) {
|
|
||||||
save_data();
|
|
||||||
display_element.unbind('click', mouse_listener); // remove the response function, so that it doesn't get triggered again.
|
|
||||||
display_element.html(''); // clear the display
|
display_element.html(''); // clear the display
|
||||||
|
|
||||||
|
save_data(info.key, info.rt);
|
||||||
|
|
||||||
if (trial.timing_post_trial > 0) {
|
if (trial.timing_post_trial > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
block.next();
|
block.next();
|
||||||
@ -83,26 +54,35 @@
|
|||||||
else {
|
else {
|
||||||
block.next();
|
block.next();
|
||||||
} // call block.next() to advance the experiment after a delay.
|
} // call block.next() to advance the experiment after a delay.
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var mouse_listener = function(e) {
|
||||||
|
|
||||||
|
var rt = (new Date()).getTime() - start_time;
|
||||||
|
|
||||||
|
display_element.unbind('click', mouse_listener);
|
||||||
|
|
||||||
|
after_response({key: 'mouse', rt: rt});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if key is 'mouse'
|
// check if key is 'mouse'
|
||||||
if (trial.cont_key == 'mouse') {
|
if (trial.cont_key == 'mouse') {
|
||||||
display_element.click(mouse_listener);
|
display_element.click(mouse_listener);
|
||||||
}
|
var start_time = (new Date()).getTime();
|
||||||
else {
|
} else {
|
||||||
// attach the response function to the html document.
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.cont_key);
|
||||||
$(document).keydown(key_down_listener);
|
|
||||||
$(document).keyup(key_listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var save_data = function() {
|
function save_data(key, rt) {
|
||||||
var rt = (new Date()).getTime() - startTime;
|
|
||||||
block.writeData($.extend({}, {
|
block.writeData($.extend({}, {
|
||||||
"trial_type": "text",
|
"trial_type": "text",
|
||||||
"trial_index": block.trial_idx,
|
"trial_index": block.trial_idx,
|
||||||
"rt": rt
|
"rt": rt,
|
||||||
|
"key_press": key
|
||||||
}, trial.data));
|
}, trial.data));
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return plugin;
|
return plugin;
|
||||||
|
202
examples/scripts/plugins/jspsych-visual-search-circle.js
Normal file
202
examples/scripts/plugins/jspsych-visual-search-circle.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/**
|
||||||
|
* jspsych-visual-search-circle
|
||||||
|
* Josh de Leeuw
|
||||||
|
*
|
||||||
|
* display a set of objects, with or without a target, equidistant from fixation
|
||||||
|
* subject responds to whether or not the target is present
|
||||||
|
*
|
||||||
|
* based on code written for psychtoolbox by Ben Motz
|
||||||
|
*
|
||||||
|
* todo:
|
||||||
|
*
|
||||||
|
* allow for use of display_element
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
jsPsych["visual-search-circle"] = (function() {
|
||||||
|
|
||||||
|
var plugin = {};
|
||||||
|
|
||||||
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
var trials = new Array(params.target_present.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < trials.length; i++) {
|
||||||
|
trials[i] = {};
|
||||||
|
trials[i].type = "visual-search-circle";
|
||||||
|
trials[i].target_present = params.target_present[i];
|
||||||
|
trials[i].set_size = params.set_size[i];
|
||||||
|
trials[i].target = params.target;
|
||||||
|
trials[i].foil = params.foil;
|
||||||
|
trials[i].fixation_image = params.fixation_image;
|
||||||
|
trials[i].target_size = params.target_size || [50, 50];
|
||||||
|
trials[i].fixation_size = params.fixation_size || [16, 16];
|
||||||
|
trials[i].circle_diameter = params.circle_diameter || 250;
|
||||||
|
trials[i].target_present_key = params.target_present_key || 74;
|
||||||
|
trials[i].target_absent_key = params.target_absent_key || 70;
|
||||||
|
trials[i].timing_max_search = (typeof params.timing_max_search === 'undefined') ? -1 : params.timing_max_search;
|
||||||
|
trials[i].timing_fixation = (typeof params.timing_fixation === 'undefined') ? 1000 : params.timing_fixation;
|
||||||
|
trials[i].timing_post_trial = (typeof params.timing_post_trial === 'undefined') ? 1000 : params.timing_post_trial;
|
||||||
|
trials[i].data = (typeof params.data === 'undefined') ? {} : params.data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return trials;
|
||||||
|
};
|
||||||
|
|
||||||
|
plugin.trial = function(display_element, block, trial, part) {
|
||||||
|
|
||||||
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
|
// screen information
|
||||||
|
var screenw = $(window).width();
|
||||||
|
var screenh = $(window).height();
|
||||||
|
var centerx = screenw / 2;
|
||||||
|
var centery = screenh / 2;
|
||||||
|
|
||||||
|
// circle params
|
||||||
|
var diam = trial.circle_diameter; // pixels
|
||||||
|
var radi = diam / 2;
|
||||||
|
var paper_size = diam + trial.target_size[0];
|
||||||
|
|
||||||
|
// stimuli width, height
|
||||||
|
var stimh = trial.target_size[0];
|
||||||
|
var stimw = trial.target_size[1];
|
||||||
|
var hstimh = stimh / 2;
|
||||||
|
var hstimw = stimw / 2;
|
||||||
|
|
||||||
|
// fixation location
|
||||||
|
var fix_loc = [Math.floor(paper_size / 2 - trial.fixation_size[0] / 2), Math.floor(paper_size / 2 - trial.fixation_size[1] / 2)];
|
||||||
|
|
||||||
|
// possible stimulus locations on the circle
|
||||||
|
var display_locs = [];
|
||||||
|
var possible_display_locs = trial.set_size;
|
||||||
|
var random_offset = Math.floor(Math.random()*360);
|
||||||
|
for(var i = 0; i < possible_display_locs; i++){
|
||||||
|
display_locs.push([
|
||||||
|
Math.floor(paper_size / 2 + (cosd(random_offset + (i * (360/possible_display_locs))) * radi) - hstimw),
|
||||||
|
Math.floor(paper_size / 2 - (sind(random_offset + (i * (360/possible_display_locs))) * radi) - hstimh)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get target to draw on
|
||||||
|
var paper = Raphael(centerx - paper_size / 2, centery - paper_size / 2, paper_size, paper_size);
|
||||||
|
|
||||||
|
show_fixation();
|
||||||
|
|
||||||
|
function show_fixation() {
|
||||||
|
// show fixation
|
||||||
|
var fixation = paper.image(trial.fixation_image, fix_loc[0], fix_loc[1], trial.fixation_size[0], trial.fixation_size[1]);
|
||||||
|
|
||||||
|
// wait
|
||||||
|
setTimeout(function() {
|
||||||
|
// after wait is over
|
||||||
|
show_search_array();
|
||||||
|
}, trial.timing_fixation);
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_search_array() {
|
||||||
|
|
||||||
|
var search_array_images = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < display_locs.length; i++) {
|
||||||
|
|
||||||
|
var which_image = (i == 0 && trial.target_present) ? trial.target : trial.foil;
|
||||||
|
|
||||||
|
var img = paper.image(which_image, display_locs[i][0], display_locs[i][1], trial.target_size[0], trial.target_size[1]);
|
||||||
|
|
||||||
|
search_array_images.push(img);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var trial_over = false;
|
||||||
|
|
||||||
|
var after_response = function(info){
|
||||||
|
|
||||||
|
trial_over = true;
|
||||||
|
|
||||||
|
var correct = 0;
|
||||||
|
|
||||||
|
if (info.key == trial.target_present_key && trial.target_present ||
|
||||||
|
info.key == trial.target_absent_key && !trial.target_present) {
|
||||||
|
correct = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_display();
|
||||||
|
|
||||||
|
end_trial(info.rt, correct, info.key);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var valid_keys = [trial.target_present_key, trial.target_absent_key];
|
||||||
|
|
||||||
|
key_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, valid_keys, 'date',false);
|
||||||
|
|
||||||
|
if (trial.timing_max_search > 0) {
|
||||||
|
setTimeout(function() {
|
||||||
|
|
||||||
|
if (!trial_over) {
|
||||||
|
|
||||||
|
jsPsych.pluginAPI.cancelKeyboardResponse(key_listener);
|
||||||
|
|
||||||
|
trial_over = true;
|
||||||
|
|
||||||
|
var rt = -1;
|
||||||
|
var correct = 0;
|
||||||
|
var key_press = -1;
|
||||||
|
|
||||||
|
clear_display();
|
||||||
|
|
||||||
|
end_trial(rt, correct, key_press);
|
||||||
|
}
|
||||||
|
}, trial.timing_max_search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_display() {
|
||||||
|
paper.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function end_trial(rt, correct, key_press) {
|
||||||
|
|
||||||
|
// data saving
|
||||||
|
var trial_data = {
|
||||||
|
trial_type: trial.type,
|
||||||
|
trial_index: block.trial_idx,
|
||||||
|
correct: correct,
|
||||||
|
rt: rt,
|
||||||
|
key_press: key_press,
|
||||||
|
locations: JSON.stringify(display_locs),
|
||||||
|
target_present: trial.target_present,
|
||||||
|
set_size: trial.set_size
|
||||||
|
};
|
||||||
|
|
||||||
|
// this line merges together the trial_data object and the generic
|
||||||
|
// data object (trial.data), and then stores them.
|
||||||
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
|
|
||||||
|
// go to next trial
|
||||||
|
if(trial.timing_post_trial > 0){
|
||||||
|
setTimeout(function() {
|
||||||
|
block.next();
|
||||||
|
}, trial.timing_post_trial);
|
||||||
|
} else {
|
||||||
|
block.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// helper function for determining stimulus locations
|
||||||
|
|
||||||
|
function cosd(num) {
|
||||||
|
return Math.cos(num / 180 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sind(num) {
|
||||||
|
return Math.sin(num / 180 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin;
|
||||||
|
})();
|
||||||
|
})(jQuery);
|
@ -6,20 +6,8 @@
|
|||||||
* Psychology: Learning, Memory, and Cognition, 28(3), 458.
|
* Psychology: Learning, Memory, and Cognition, 28(3), 458.
|
||||||
*
|
*
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* February 2014
|
|
||||||
*
|
*
|
||||||
* REQUIRES rapaheljs (www.raphaeljs.com)
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-vsl-animate-occlusion
|
||||||
*
|
|
||||||
* parameters:
|
|
||||||
* stimuli: array of paths to images. will be shown in the order declared in array
|
|
||||||
* timing_cycle: how long for an image to complete an animation cycle
|
|
||||||
* canvas_size: array [width, height] - how big to draw the area
|
|
||||||
* image_size: array [width, height] - how big to draw the stimuli
|
|
||||||
* initial_direction: "left" or "right" - which way to move the first image
|
|
||||||
* occlude_center: if true, draw a rectangle in the center to occlude the swaps between images
|
|
||||||
* timing_pre_movement: how long to wait before the first image starts moving
|
|
||||||
* timing_post_trial: how long to show blank screen after trial
|
|
||||||
* data: the optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -29,6 +17,7 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
var trials = new Array(1);
|
var trials = new Array(1);
|
||||||
|
|
||||||
trials[0] = {};
|
trials[0] = {};
|
||||||
@ -54,7 +43,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
//trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
// variable to keep track of timing info and responses
|
// variable to keep track of timing info and responses
|
||||||
var start_time = 0;
|
var start_time = 0;
|
||||||
@ -133,18 +122,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add key listener
|
// add key listener
|
||||||
var resp_func = function(e){
|
var after_response = function(info){
|
||||||
for(var i=0; i<trial.choices.length; i++){
|
responses.push({
|
||||||
if(e.which == trial.choices[i]){
|
key: info.key,
|
||||||
responses.push({
|
stimulus: which_image - 1,
|
||||||
key: e.which,
|
rt: info.rt
|
||||||
stimulus: which_image - 1,
|
});
|
||||||
rt: (new Date()).getTime() - start_time
|
}
|
||||||
});
|
|
||||||
}
|
key_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', true);
|
||||||
}
|
|
||||||
};
|
|
||||||
$(document).keydown(resp_func);
|
|
||||||
|
|
||||||
if (trial.timing_pre_movement > 0) {
|
if (trial.timing_pre_movement > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -159,7 +145,7 @@
|
|||||||
|
|
||||||
display_element.html('');
|
display_element.html('');
|
||||||
|
|
||||||
$(document).unbind('keydown', resp_func);
|
jsPsych.pluginAPI.cancelKeyboardResponse(key_listener);
|
||||||
|
|
||||||
block.writeData($.extend({}, {
|
block.writeData($.extend({}, {
|
||||||
"trial_type": "vsl-animate-occlusion",
|
"trial_type": "vsl-animate-occlusion",
|
||||||
|
@ -6,25 +6,8 @@
|
|||||||
* 12(6), 499-504.
|
* 12(6), 499-504.
|
||||||
*
|
*
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* February 2014
|
|
||||||
*
|
*
|
||||||
*
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-vsl-grid-scene
|
||||||
* parameters:
|
|
||||||
* stimuli: array of arrays describing scenes. each interior array should have dimensions
|
|
||||||
* equal to the size of the desired grid. for example, a 3 x 3 grid with stimuli along
|
|
||||||
* the top-left to bottom-right diagonal would be declared like this:
|
|
||||||
*
|
|
||||||
* var s = [
|
|
||||||
* [ "img_path", 0, 0 ],
|
|
||||||
* [ 0, "img_path", 0 ],
|
|
||||||
* [ 0, 0, "img_path" ]
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* for blank spaces in the grid, you need to put a 0 in the corresponding location.
|
|
||||||
* image_size: array [width, height] - how big to draw the stimuli
|
|
||||||
* timing_duration: how long to show the scene
|
|
||||||
* timing_post_trial: how long to show blank screen after trial
|
|
||||||
* data: the optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -34,6 +17,8 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data'])
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
for (var i = 0; i < trials.length; i++) {
|
for (var i = 0; i < trials.length; i++) {
|
||||||
trials[i] = {};
|
trials[i] = {};
|
||||||
@ -53,7 +38,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
display_element.html(plugin.generate_stimulus(trial.stimuli, trial.image_size));
|
display_element.html(plugin.generate_stimulus(trial.stimuli, trial.image_size));
|
||||||
|
|
||||||
@ -153,4 +138,4 @@
|
|||||||
|
|
||||||
return plugin;
|
return plugin;
|
||||||
})();
|
})();
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -1,27 +1,10 @@
|
|||||||
/* jspsych-xab.js
|
/* jspsych-xab.js
|
||||||
* Josh de Leeuw
|
* Josh de Leeuw
|
||||||
* updated Oct 2013
|
|
||||||
*
|
*
|
||||||
* This plugin runs a single XAB trial, where X is an image presented in isolation, and A and B are choices, with A or B being equal to X.
|
* This plugin runs a single XAB trial, where X is an image presented in isolation, and A and B are choices, with A or B being equal to X.
|
||||||
* The subject's goal is to identify whether A or B is identical to X.
|
* The subject's goal is to identify whether A or B is identical to X.
|
||||||
*
|
*
|
||||||
* parameters:
|
* documentation: https://github.com/jodeleeuw/jsPsych/wiki/jspsych-xab
|
||||||
* stimuli: array of arrays. each interior array represents the stimuli for a single trial.
|
|
||||||
* each interior array can be two or three elements. if two elements, then the plugin
|
|
||||||
* will show the first one as the target (X). if three elements, then the first is X
|
|
||||||
* the second is A and the third is B. the second is considered the target and the third
|
|
||||||
* is the foil. this is useful if X and A are not identical, but A is still the correct
|
|
||||||
* choice (e.g. a categorization experiment where the goal is to pick the item that is
|
|
||||||
* in the same category). stimuli can be paths to images, or html strings.
|
|
||||||
* left_key: key code for response associated with image on the left side of the screen.
|
|
||||||
* right_key: key code for right side
|
|
||||||
* timing_x: how long to display X for in ms
|
|
||||||
* timing_xab_gap: how long to show a blank screen in between X and AB in ms.
|
|
||||||
* timing_ab: how long to show the screen with AB in ms. -1 will display until a response is given.
|
|
||||||
* timing_post_trial: how long to show a blank screen after the trial is complete.
|
|
||||||
* is_html: must set to TRUE if the stimuli are HTML strings instead of images.
|
|
||||||
* prompt: an HTML string to display under the AB stimuli, e.g. to remind the subject which keys to use
|
|
||||||
* data: the optional data object
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -31,6 +14,8 @@
|
|||||||
var plugin = {};
|
var plugin = {};
|
||||||
|
|
||||||
plugin.create = function(params) {
|
plugin.create = function(params) {
|
||||||
|
|
||||||
|
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
|
||||||
|
|
||||||
// the number of trials is determined by how many entries the params.stimuli array has
|
// the number of trials is determined by how many entries the params.stimuli array has
|
||||||
var trials = new Array(params.stimuli.length);
|
var trials = new Array(params.stimuli.length);
|
||||||
@ -72,7 +57,7 @@
|
|||||||
// if any trial variables are functions
|
// if any trial variables are functions
|
||||||
// this evaluates the function and replaces
|
// this evaluates the function and replaces
|
||||||
// it with the output of the function
|
// it with the output of the function
|
||||||
trial = jsPsych.normalizeTrialVariables(trial);
|
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
|
||||||
|
|
||||||
switch (part) {
|
switch (part) {
|
||||||
|
|
||||||
@ -127,20 +112,20 @@
|
|||||||
if (!trial.is_html) {
|
if (!trial.is_html) {
|
||||||
display_element.append($('<img>', {
|
display_element.append($('<img>', {
|
||||||
"src": images[0],
|
"src": images[0],
|
||||||
"class": 'jspsych-xab-stimulus'
|
"class": 'jspsych-xab-stimulus left'
|
||||||
}));
|
}));
|
||||||
display_element.append($('<img>', {
|
display_element.append($('<img>', {
|
||||||
"src": images[1],
|
"src": images[1],
|
||||||
"class": 'jspsych-xab-stimulus'
|
"class": 'jspsych-xab-stimulus right'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
display_element.append($('<div>', {
|
display_element.append($('<div>', {
|
||||||
"class": 'jspsych-xab-stimulus',
|
"class": 'jspsych-xab-stimulus left',
|
||||||
html: images[0]
|
html: images[0]
|
||||||
}));
|
}));
|
||||||
display_element.append($('<div>', {
|
display_element.append($('<div>', {
|
||||||
"class": 'jspsych-xab-stimulus',
|
"class": 'jspsych-xab-stimulus right',
|
||||||
html: images[1]
|
html: images[1]
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -149,9 +134,6 @@
|
|||||||
display_element.append(trial.prompt);
|
display_element.append(trial.prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start measuring response time
|
|
||||||
var startTime = (new Date()).getTime();
|
|
||||||
|
|
||||||
// if timing_ab is > 0, then we hide the stimuli after timing_ab milliseconds
|
// if timing_ab is > 0, then we hide the stimuli after timing_ab milliseconds
|
||||||
if (trial.timing_ab > 0) {
|
if (trial.timing_ab > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -162,52 +144,54 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the function that triggers when a key is pressed.
|
// create the function that triggers when a key is pressed.
|
||||||
var resp_func = function(e) {
|
var after_response = function(info) {
|
||||||
var flag = false; // true when a valid key is chosen
|
|
||||||
var correct = false; // true when the correct response is chosen
|
var correct = false; // true when the correct response is chosen
|
||||||
if (e.which == trial.left_key) // 'q' key by default
|
|
||||||
|
if (info.key == trial.left_key) // 'q' key by default
|
||||||
{
|
{
|
||||||
flag = true;
|
|
||||||
if (target_left) {
|
if (target_left) {
|
||||||
correct = true;
|
correct = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (e.which == trial.right_key) // 'p' key by default
|
else if (info.key == trial.right_key) // 'p' key by default
|
||||||
{
|
{
|
||||||
flag = true;
|
|
||||||
if (!target_left) {
|
if (!target_left) {
|
||||||
correct = true;
|
correct = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flag) {
|
|
||||||
var endTime = (new Date()).getTime();
|
|
||||||
var rt = (endTime - startTime);
|
// create object to store data from trial
|
||||||
// create object to store data from trial
|
var trial_data = {
|
||||||
var trial_data = {
|
"trial_type": "xab",
|
||||||
"trial_type": "xab",
|
"trial_index": block.trial_idx,
|
||||||
"trial_index": block.trial_idx,
|
"rt": info.rt,
|
||||||
"rt": rt,
|
"correct": correct,
|
||||||
"correct": correct,
|
"stimulus_x": trial.x_path,
|
||||||
"stimulus_x": trial.x_path,
|
"stimulus_a": trial.a_path,
|
||||||
"stimulus_a": trial.a_path,
|
"stimulus_b": trial.b_path,
|
||||||
"stimulus_b": trial.b_path,
|
"key_press": info.key
|
||||||
"key_press": e.which
|
};
|
||||||
};
|
block.writeData($.extend({}, trial_data, trial.data));
|
||||||
block.writeData($.extend({}, trial_data, trial.data));
|
|
||||||
$(document).unbind('keydown', resp_func); // remove response function from keys
|
display_element.html(''); // remove all
|
||||||
display_element.html(''); // remove all
|
|
||||||
xab_trial_complete = true;
|
xab_trial_complete = true;
|
||||||
// move on to the next trial after timing_post_trial milliseconds
|
|
||||||
if(trial.timing_post_trial > 0) {
|
// move on to the next trial after timing_post_trial milliseconds
|
||||||
setTimeout(function() {
|
if(trial.timing_post_trial > 0) {
|
||||||
block.next();
|
setTimeout(function() {
|
||||||
}, trial.timing_post_trial);
|
|
||||||
} else {
|
|
||||||
block.next();
|
block.next();
|
||||||
}
|
}, trial.timing_post_trial);
|
||||||
|
} else {
|
||||||
|
block.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
$(document).keydown(resp_func);
|
|
||||||
|
jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.left_key, trial.right_key], 'date', false);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user