Merge pull request #155 from jodeleeuw/dev

4.3 merge
This commit is contained in:
Josh de Leeuw 2015-06-26 20:38:32 -04:00
commit 3f727bdd0d
24 changed files with 865 additions and 493 deletions

View File

@ -2,6 +2,46 @@
The jsPsych.data module contains functions for interacting with the data generated by jsPsych plugins.
---
## jsPsych.data.addDataToLastTrial
```
jsPsych.data.addDataToLastTrial(data)
```
### Parameters
Parameter | Type | Description
----------|------|------------
data | object | Object of key: value pairs to add to the data from the last trial.
### Return value
Returns nothing.
### Description
This method appends data to the data recorded by the last trial. It's particularly useful when combined with the `on_finish` event handler for a trial, as shown in the example below.
### Examples
#### Evaluate a response and add to data
```javascript
var block = {
type: 'single-stim',
stimuli: ['img/happy_face_1.jpg', 'img/sad_face_1.jpg'],
choices: [89,78], // Y or N
prompt: '<p class="center-content">Have you seen this face before? Y or N.</p>',
on_finish: function(trial_data){
// let's imagine that the correct answer is NO for both trials
var correct = (trial_data.key_press == 78);
jsPsych.data.addDataToLastTrial({correct: correct});
}
}
```
---
## jsPsych.data.addProperties

View File

@ -100,16 +100,21 @@ plugin.create = function(params) {
## jsPsych.pluginAPI.getKeyboardResponse
```
jsPsych.pluginAPI.getKeyboardResponse(callback_function, valid_responses, rt_method, persist)
jsPsych.pluginAPI.getKeyboardResponse(parameters)
```
### Parameters
The method accepts an object of parameter values (see example below). The valid keys for this object are listed in the table below.
Parameter | Type | Description
----------|------|------------
callback_function | function | The function to execute whenever a valid keyboard response is generated.
valid_responses | array | An array of key codes or character strings representing valid responses. Responses not on the list will be ignored. An empty array indicates that all responses are acceptable.
rt_method | string | Indicates which method of recording time to use. The `'date'` method uses calls to `(new Date()).getTime()` to record timing information. The `'performance'` method uses calls to `performance.now()`, which is a more modern JavaScript feature. The `'performance'` approach is [not supported by all the major browsers yet](http://caniuse.com/#search=performance), but adoption rates are increasing.
rt_method | string | Indicates which method of recording time to use. The `'date'` method uses calls to `(new Date()).getTime()` to record timing information. The `'performance'` method uses calls to `performance.now()`, which is a more modern JavaScript feature. The `'performance'` approach is [not supported by all the major browsers yet](http://caniuse.com/#search=performance), but adoption rates are increasing. The `audio` method is used in conjuction with an `audio_context` (set as an additional parameter). This uses the clock time of the `audio_context` when audio stimuli are being played.
audio_context | AudioContext object | The AudioContext of the audio file that is being played.
audio_context_start_time | numeric | The scheduled time of the sound file in the AudioContext. This will be used as the start time.
allow_held_key | boolean | If `true`, then responses will be registered from keys that are being held down. If `false`, then a held key can only register a response the first time that `getKeyboardResponse` is called for that key. For example, if a participant holds down the `A` key before the experiment starts, then the first time `getKeyboardResponse` is called, the `A` will register as a key press. However, any future calls to `getKeyboardResponse` will not register the `A` until the participant releases the key and presses it again.
persist | boolean | If false, then the keyboard listener will only trigger the first time a valid key is pressed. If true, then it will trigger every time a valid key is pressed until it is explicitly cancelled by `jsPsych.pluginAPI.cancelKeyboardResponse` or `jsPsych.pluginAPI.cancelAllKeyboardResponses`.
### Return value
@ -131,7 +136,12 @@ var after_response = function(info){
alert('You pressed key '+info.key+' after '+info.rt+'ms');
}
jsPsych.pluginAPI.getKeyboardResponse(after_response, [], 'date', false);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function:after_response,
valid_responses: [],
rt_method: 'date',
persist: false
});
```
#### Get a responses from a key until the letter Q is pressed
@ -145,7 +155,12 @@ var after_response = function(info){
}
}
var listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, [], 'date', true);
var listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function:after_response,
valid_responses: [],
rt_method: 'date',
persist: true
});
```
---

View File

@ -275,3 +275,56 @@ var shuffledArray = jsPsych.randomization.shuffle(myArray);
// output: shuffledArray = [3,2,4,1,5]
```
---
## jsPsych.randomization.shuffleNoRepeats
```
jsPsych.randomization.shuffleNoRepeats(array, equalityTest)
```
### Parameters
Parameter | Type | Description
----------|------|------------
array | array | The array of values to shuffle
equalityTest | function | A function to use to evaluate the equality of neighbors in the array. The function should accept two parameters, which are the two elements to be tested. It should return `true` if they are equal and `false` if not. The default function, if none is specified, is to use the `===` operator. This will work for primitive values, but fail for Objects and Arrays. An example function is given below in the examples.
### Return value
Returns an array with the same elements as the input array in a random order, with no repeating neighbors.
### Description
Shuffle an array, ensuring that neighboring elements in the array are different.
*Warning: if you provide an array that has very few valid permutations with no neighboring elements, then this method will fail and cause the browser to hang.*
### Examples
#### Basic example
```javascript
var myArray = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5];
var shuffledArray = jsPsych.randomization.shuffleNoRepeats(myArray);
// output: shuffledArray = [2, 3, 5, 1, 2, 4, 1, 5, 4, 1, 3, 5, 4, 3, 2]
```
#### Custom equalityTest
```javascript
var myObjects = [
{color:"blue"},
{color:"red"},
{color:"yellow"},
{color:"orange"}
];
var repeatedSet = jsPsych.randomization.repeat(myObjects,3);
var shuffled = jsPsych.randomization.shuffleNoRepeats(repeatedSet, function(a,b) { return a.color === b.color });
// console.log(JSON.stringify(shuffled))
// "[{"color":"red"},{"color":"yellow"},{"color":"blue"},{"color":"yellow"},{"color":"orange"},{"color":"red"},{"color":"yellow"},{"color":"orange"},{"color":"blue"},{"color":"orange"},{"color":"red"},{"color":"blue"}]"
```

View File

@ -21,6 +21,7 @@ Every jsPsych experiment utilizes the core library (contained in the `jspsych.js
### [Data module](jspsych-data.md)
* [jsPsych.data.addDataToLastTrial](jspsych-data.md#jspsychdataadddatatolasttrial)
* [jsPsych.data.addProperties](jspsych-data.md#jspsychdataaddproperties)
* [jsPsych.data.dataAsCSV](jspsych-data.md#jspsychdatadataascsv)
* [jsPsych.data.displayData](jspsych-data.md#jspsychdatadisplaydata)
@ -43,6 +44,7 @@ Every jsPsych experiment utilizes the core library (contained in the `jspsych.js
* [jsPsych.randomization.repeat](jspsych-randomization.md#jspsychrandomizationrepeat)
* [jsPsych.randomization.sample](jspsych-randomization.md#jspsychrandomizationsample)
* [jsPsych.randomization.shuffle](jspsych-randomization.md#jspsychrandomizationshuffle)
* [jsPsych.randomization.shuffleNoRepeats](jspsych-randomization.md#jspsychrandomizationshufflenorepeats)
### [PluginAPI module](jspsych-pluginAPI.md)

View File

@ -18,8 +18,12 @@ incorrect_text | string | "Wrong." | String to show when the wrong answer is giv
prompt | string | "" | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g. which key to press).
force_correct_button_press | boolean | false | If set to true, then the subject must press the correct response key after feedback is given in order to advance to the next trial.
show_stim_with_feedback | boolean | true | If set to true, then the stimulus will be shown during feedback. If false, then only the text feedback will display during feedback.
show_feedback_on_timeout | boolean | false | If true, then category feedback will be displayed for an incorrect response after a timeout (timing_response is exceeded). If false, then a timeout message will be shown.
timeout_message | string | "Please respond faster." | The message to show on a timeout non-response.
timing_stim | numeric | -1 | How long to show the stimulus for (milliseconds). If -1, then the stimulus is shown until a response is given.
timing_feedback_duration | numeric | 2000 | How long to show the feedback for (milliseconds).
timing_response | numeric | -1 | The maximum time allowed for a response. If -1, then the experiment will wait indefinitely for a response.
## Data Generated

View File

@ -10,6 +10,8 @@ Parameter | Type | Default Value | Description
----------|------|---------------|------------
questions | array | *undefined* | Each array is an array of strings. The strings are the prompts for the subject to respond to. Each string gets its own response field. Each set of strings (inner arrays) will be presented on the same page (trial). The length of the outer array sets the number of trials in the block.
preamble | string | empty string | HTML formatted string to display at the top of the page above all the questions.
rows | array | 1 | The number of rows for the response text box. Array dimensions must match `questions` array, with a numeric value for each entry indicating the number of rows for that question's box.
columns | array | 40 | The number of columns for the response text box. Array dimensions must match `questions` array, with a numeric value for each entry indicating the number of columns for that question's box.
## Data Generated
@ -34,3 +36,18 @@ var survey_block = {
questions: [page_1_questions, page_2_questions],
};
```
### Custom number of rows and columns
```javascript
// defining groups of questions that will go together.
var page_1_questions = ["How old are you?", "Where were you born?"];
var page_2_questions = ["What is your favorite food?"];
var survey_block = {
type: 'survey-text',
questions: [page_1_questions, page_2_questions],
rows: [[5,3],[2]],
columns: [[40,50],[60]]
};
```

View File

@ -645,7 +645,13 @@
// now add to list so that it gets appended to all future data
dataProperties = $.extend({}, dataProperties, properties);
};
module.addDataToLastTrial = function(data){
if(allData.length == 0){
throw new Error("Cannot add data to last trial - no data recorded so far");
}
allData[allData.length-1] = $.extend({},allData[allData.length-1],data);
}
module.dataAsCSV = function() {
@ -906,7 +912,7 @@
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.');
console.log('Unclear parameters given to randomization.repeat. Multiple set sizes specified, but only one item exists to sample. Proceeding using the first set size.');
}
} else {
if (!rep_isArray) {
@ -917,8 +923,18 @@
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
console.warning('Unclear parameters given to randomization.repeat. Items and repetitions are unequal lengths. Behavior may not be as expected.');
// throw warning if repetitions is too short, use first rep ONLY.
if(repetitions.length < array.length){
var reps = [];
for (var i = 0; i < array.length; i++) {
reps.push(repetitions);
}
repetitions = reps;
} else {
// throw warning if too long, and then use the first N
repetitions = repetions.slice(0, array.length);
}
}
}
}
@ -944,6 +960,36 @@
return shuffle(arr);
}
module.shuffleNoRepeats = function(arr, equalityTest){
// define a default equalityTest
if(typeof equalityTest == 'undefined'){
equalityTest = function(a,b){
if(a === b) { return true; }
else { return false; }
}
}
var random_shuffle = shuffle(arr);
for(var i=0; i<random_shuffle.length-2; i++){
if(equalityTest(random_shuffle[i], random_shuffle[i+1])){
// neighbors are equal, pick a new random neighbor to swap (not the first or last element, to avoid edge cases)
var random_pick = Math.floor(Math.random()*(random_shuffle.length-2))+1;
// test to make sure the new neighbor isn't equal to the old one
while(
equalityTest(random_shuffle[i+1], random_shuffle[random_pick]) ||
(equalityTest(random_shuffle[i+1], random_shuffle[random_pick+1]) || equalityTest(random_shuffle[i+1], random_shuffle[random_pick-1]))
){
random_pick = Math.floor(Math.random()*(random_shuffle.length-2))+1;
}
var new_neighbor = random_shuffle[random_pick];
random_shuffle[random_pick] = random_shuffle[i+1];
random_shuffle[i+1] = new_neighbor;
}
}
return random_shuffle;
}
module.sample = function(arr, size, withReplacement) {
if(withReplacement == false) {
if(size > arr.length){
@ -1051,23 +1097,26 @@
// keyboard listeners
var keyboard_listeners = [];
var held_keys = [];
var module = {};
module.getKeyboardResponse = function(callback_function, valid_responses, rt_method, persist, audio_context, audio_context_start_time) {
module.getKeyboardResponse = function(parameters){
//parameters are: callback_function, valid_responses, rt_method, persist, audio_context, audio_context_start_time, allow_held_key?
rt_method = (typeof rt_method === 'undefined') ? 'date' : rt_method;
if (rt_method != 'date' && rt_method != 'performance' && rt_method != 'audio') {
parameters.rt_method = (typeof parameters.rt_method === 'undefined') ? 'date' : parameters.rt_method;
if (parameters.rt_method != 'date' && parameters.rt_method != 'performance' && parameters.rt_method != 'audio') {
console.log('Invalid RT method specified in getKeyboardResponse. Defaulting to "date" method.');
rt_method = 'date';
parameters.rt_method = 'date';
}
var start_time;
if (rt_method == 'date') {
if (parameters.rt_method == 'date') {
start_time = (new Date()).getTime();
} else if (rt_method == 'performance') {
} else if (parameters.rt_method == 'performance') {
start_time = performance.now();
} else if (rt_method == 'audio') {
start_time = audio_context_start_time;
} else if (parameters.rt_method == 'audio') {
start_time = parameters.audio_context_start_time;
}
var listener_id;
@ -1075,51 +1124,66 @@
var listener_function = function(e) {
var key_time;
if (rt_method == 'date') {
if (parameters.rt_method == 'date') {
key_time = (new Date()).getTime();
} else if (rt_method == 'performance') {
} else if (parameters.rt_method == 'performance') {
key_time = performance.now();
} else if (rt_method == 'audio') {
key_time = audio_context.currentTime
} else if (parameters.rt_method == 'audio') {
key_time = parameters.audio_context.currentTime
}
var valid_response = false;
if (typeof valid_responses === 'undefined' || valid_responses.length === 0) {
if (typeof parameters.valid_responses === 'undefined' || parameters.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]]) {
for (var i = 0; i < parameters.valid_responses.length; i++) {
if (typeof parameters.valid_responses[i] == 'string') {
if (typeof keylookup[parameters.valid_responses[i]] !== 'undefined') {
if (e.which == keylookup[parameters.valid_responses[i]]) {
valid_response = true;
}
} else {
throw new Error('Invalid key string specified for getKeyboardResponse');
}
} else if (e.which == valid_responses[i]) {
} else if (e.which == parameters.valid_responses[i]) {
valid_response = true;
}
}
// check if key was already held down
if ( ((typeof parameters.allow_held_key == 'undefined') || !parameters.allow_held_key) && valid_response ) {
for(i in held_keys){
if(held_keys[i]==e.which){
valid_response = false;
break;
}
}
}
if (valid_response) {
held_keys.push(e.which);
parameters.callback_function({
key: e.which,
rt: key_time - start_time
});
if ($.inArray(listener_id, keyboard_listeners) > -1) {
if (!parameters.persist) {
// remove keyboard listener
module.cancelKeyboardResponse(listener_id);
}
}
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
});
}
// mark key as released
held_keys.splice($.inArray(e.which, held_keys), 1);
}
};

View File

@ -108,7 +108,13 @@
// 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);
var response_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'date',
persist: true,
allow_held_key: false
});
function endTrial() {

View File

@ -139,7 +139,13 @@
}
keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', true);
keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'date',
persist: true,
allow_held_key: false
});
function endTrial() {
clearInterval(animate_interval); // stop animation!

View File

@ -23,14 +23,16 @@
trials[i].choices = params.choices;
trials[i].correct_text = (typeof params.correct_text === 'undefined') ? "<p class='feedback'>Correct</p>" : params.correct_text;
trials[i].incorrect_text = (typeof params.incorrect_text === 'undefined') ? "<p class='feedback'>Incorrect</p>" : params.incorrect_text;
// timing params
trials[i].timing_stim = params.timing_stim || -1; // default is to show image until response
trials[i].timing_feedback_duration = params.timing_feedback_duration || 2000;
// optional params
trials[i].show_stim_with_feedback = (typeof params.show_stim_with_feedback === 'undefined') ? true : params.show_stim_with_feedback;
trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
trials[i].force_correct_button_press = (typeof params.force_correct_button_press === 'undefined') ? false : params.force_correct_button_press;
trials[i].prompt = (typeof params.prompt === 'undefined') ? '' : params.prompt;
trials[i].show_feedback_on_timeout = (typeof params.show_feedback_on_timeout === 'undefined') ? false : params.show_feedback_on_timeout;
trials[i].timeout_message = params.timeout_message || "<p>Please respond faster.</p>";
// timing params
trials[i].timing_stim = params.timing_stim || -1; // default is to show image until response
trials[i].timing_response = params.timing_response || -1; // default is no max response time
trials[i].timing_feedback_duration = params.timing_feedback_duration || 2000;
}
return trials;
};
@ -81,6 +83,9 @@
clearTimeout(setTimeoutHandlers[i]);
}
// clear keyboard listener
jsPsych.pluginAPI.cancelAllKeyboardResponses();
var correct = false;
if (trial.key_answer == info.key) {
correct = true;
@ -98,51 +103,72 @@
display_element.html('');
doFeedback(correct);
var timeout = info.rt == -1;
doFeedback(correct, timeout);
}
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', false);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'date',
persist: false,
allow_held_key: false
});
if(trial.timing_response > 0) {
setTimeoutHandlers.push(setTimeout(function(){
after_response({key: -1, rt: -1});
}, trial.timing_response));
}
function doFeedback(correct, timeout) {
function doFeedback(correct) {
// show image during feedback if flag is set
if (trial.show_stim_with_feedback) {
if (!trial.is_html) {
// add image to display
display_element.append($('<img>', {
"src": trial.a_path,
"class": 'jspsych-categorize-stimulus',
"id": 'jspsych-categorize-stimulus'
}));
} else {
display_element.append($('<div>', {
"id": 'jspsych-categorize-stimulus',
"class": 'jspsych-categorize-stimulus',
"html": trial.a_path
}));
}
}
// substitute answer in feedback string.
var atext = "";
if (correct) {
atext = trial.correct_text.replace("%ANS%", trial.text_answer);
if(timeout && !trial.show_feedback_on_timeout){
display_element.append(trial.timeout_message);
} else {
atext = trial.incorrect_text.replace("%ANS%", trial.text_answer);
// show image during feedback if flag is set
if (trial.show_stim_with_feedback) {
if (!trial.is_html) {
// add image to display
display_element.append($('<img>', {
"src": trial.a_path,
"class": 'jspsych-categorize-stimulus',
"id": 'jspsych-categorize-stimulus'
}));
} else {
display_element.append($('<div>', {
"id": 'jspsych-categorize-stimulus',
"class": 'jspsych-categorize-stimulus',
"html": trial.a_path
}));
}
}
// substitute answer in feedback string.
var atext = "";
if (correct) {
atext = trial.correct_text.replace("%ANS%", trial.text_answer);
} else {
atext = trial.incorrect_text.replace("%ANS%", trial.text_answer);
}
// show the feedback
display_element.append(atext);
}
// show the feedback
display_element.append(atext);
// check if force correct button press is set
if (trial.force_correct_button_press && correct === false) {
if (trial.force_correct_button_press && correct === false && ((timeout && trial.show_feedback_on_timeout) || !timeout)) {
var after_forced_response = function(info) {
endTrial();
}
jsPsych.pluginAPI.getKeyboardResponse(after_forced_response, trial.key_answer, 'date', false);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_forced_response,
valid_responses: [trial.key_answer],
rt_method: 'date',
persist: false,
allow_held_key: false
});
} else {
setTimeout(function() {

View File

@ -10,154 +10,164 @@
*/
(function($) {
jsPsych.instructions = (function() {
jsPsych.instructions = (function() {
var plugin = {};
var plugin = {};
plugin.create = function(params) {
plugin.create = function(params) {
params = jsPsych.pluginAPI.enforceArray(params, ['pages']);
params = jsPsych.pluginAPI.enforceArray(params, ['pages']);
var trials = new Array(1);
var trials = new Array(1);
trials[0] = {};
trials[0].pages = params.pages;
trials[0].key_forward = params.key_forward || 'rightarrow';
trials[0].key_backward = params.key_backward || 'leftarrow';
trials[0].allow_backward = (typeof params.allow_backward === 'undefined') ? true : params.allow_backward;
trials[0].allow_keys = (typeof params.allow_keys === 'undefined') ? true : params.allow_keys;
trials[0].show_clickable_nav = (typeof params.show_clickable_nav === 'undefined') ? false : params.show_clickable_nav;
trials[0] = {};
trials[0].pages = params.pages;
trials[0].key_forward = params.key_forward || 'rightarrow';
trials[0].key_backward = params.key_backward || 'leftarrow';
trials[0].allow_backward = (typeof params.allow_backward === 'undefined') ? true : params.allow_backward;
trials[0].allow_keys = (typeof params.allow_keys === 'undefined') ? true : params.allow_keys;
trials[0].show_clickable_nav = (typeof params.show_clickable_nav === 'undefined') ? false : params.show_clickable_nav;
return trials;
};
return trials;
};
plugin.trial = function(display_element, trial) {
plugin.trial = function(display_element, trial) {
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
var current_page = 0;
var current_page = 0;
var view_history = [];
var view_history = [];
var start_time = (new Date()).getTime();
var start_time = (new Date()).getTime();
var last_page_update_time = start_time;
var last_page_update_time = start_time;
function show_current_page(){
display_element.html(trial.pages[current_page]);
function show_current_page() {
display_element.html(trial.pages[current_page]);
if(trial.show_clickable_nav){
if (trial.show_clickable_nav) {
var nav_html = "<div class='jspsych-instructions-nav'>";
if(current_page != 0 && trial.allow_backward){
nav_html += "<button id='jspsych-instructions-back'>&lt; Previous</button>";
}
nav_html += "<button id='jspsych-instructions-next'>Next &gt;</button></div>"
var nav_html = "<div class='jspsych-instructions-nav'>";
if (current_page != 0 && trial.allow_backward) {
nav_html += "<button id='jspsych-instructions-back'>&lt; Previous</button>";
}
nav_html += "<button id='jspsych-instructions-next'>Next &gt;</button></div>"
display_element.append(nav_html);
display_element.append(nav_html);
if(current_page != 0 && trial.allow_backward){
$('#jspsych-instructions-back').on('click',function(){
clear_button_handlers();
back();
});
}
if (current_page != 0 && trial.allow_backward) {
$('#jspsych-instructions-back').on('click', function() {
clear_button_handlers();
back();
});
}
$('#jspsych-instructions-next').on('click',function(){
clear_button_handlers();
next();
});
$('#jspsych-instructions-next').on('click', function() {
clear_button_handlers();
next();
});
}
}
}
}
function clear_button_handlers() {
$('#jspsych-instructions-next').off('click');
$('#jspsych-instructions-back').off('click');
}
function clear_button_handlers() {
$('#jspsych-instructions-next').off('click');
$('#jspsych-instructions-back').off('click');
}
function next() {
function next() {
add_current_page_to_view_history()
add_current_page_to_view_history()
current_page++;
current_page++;
// if done, finish up...
if(current_page >= trial.pages.length) {
endTrial();
} else {
show_current_page();
}
// if done, finish up...
if (current_page >= trial.pages.length) {
endTrial();
} else {
show_current_page();
}
}
}
function back() {
function back() {
add_current_page_to_view_history()
add_current_page_to_view_history()
current_page--;
current_page--;
show_current_page();
}
show_current_page();
}
function add_current_page_to_view_history(){
function add_current_page_to_view_history() {
var current_time = (new Date()).getTime();
var current_time = (new Date()).getTime();
var page_view_time = current_time - last_page_update_time;
var page_view_time = current_time - last_page_update_time;
view_history.push({
page_index: current_page,
viewing_time: page_view_time
});
view_history.push({
page_index: current_page,
viewing_time: page_view_time
});
last_page_update_time = current_time;
}
last_page_update_time = current_time;
}
function endTrial() {
function endTrial() {
if(trial.allow_keys){
jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
}
if (trial.allow_keys) {
jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
}
display_element.html('');
display_element.html('');
jsPsych.data.write({
"view_history": JSON.stringify(view_history),
"rt": (new Date()).getTime() - start_time
});
jsPsych.data.write({
"view_history": JSON.stringify(view_history),
"rt": (new Date()).getTime() - start_time
});
jsPsych.finishTrial();
}
jsPsych.finishTrial();
}
var after_response = function(info) {
var after_response = function(info) {
// have to reinitialize this instead of letting it persist to prevent accidental skips of pages by holding down keys too long
keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.key_forward, trial.key_backward]);
// have to reinitialize this instead of letting it persist to prevent accidental skips of pages by holding down keys too long
keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: [trial.key_forward, trial.key_backward],
rt_method: 'date',
persist: false,
allow_held_key: false
});
// check if key is forwards or backwards and update page
if (info.key === trial.key_backward || info.key === jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.key_backward)) {
if (current_page !== 0 && trial.allow_backward) {
back();
}
}
// check if key is forwards or backwards and update page
if(info.key === trial.key_backward || info.key === jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.key_backward)){
if(current_page !== 0 && trial.allow_backward) {
back();
}
}
if (info.key === trial.key_forward || info.key === jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.key_forward)) {
next();
}
if(info.key === trial.key_forward || info.key === jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.key_forward)){
next();
}
};
};
show_current_page();
show_current_page();
if (trial.allow_keys) {
var keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: [trial.key_forward, trial.key_backward],
rt_method: 'date',
persist: false
});
}
};
if(trial.allow_keys){
var keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.key_forward, trial.key_backward]);
}
};
return plugin;
})();
return plugin;
})();
})(jQuery);

View File

@ -1,212 +1,218 @@
/**
* jspsych-muli-stim-multi-response
* Josh de Leeuw
*
* plugin for displaying a set of stimuli and collecting a set of responses
* via the keyboard
*
* documentation: docs.jspsych.org
*
**/
* jspsych-muli-stim-multi-response
* Josh de Leeuw
*
* plugin for displaying a set of stimuli and collecting a set of responses
* via the keyboard
*
* documentation: docs.jspsych.org
*
**/
(function($) {
jsPsych["multi-stim-multi-response"] = (function() {
jsPsych["multi-stim-multi-response"] = (function() {
var plugin = {};
var plugin = {};
plugin.create = function(params) {
plugin.create = function(params) {
var trials = new Array(params.stimuli.length);
var trials = new Array(params.stimuli.length);
for (var i = 0; i < trials.length; i++) {
trials[i] = {};
trials[i].stimuli = params.stimuli[i];
trials[i].choices = params.choices;
// option to show image for fixed time interval, ignoring key responses
// true = image will keep displaying after response
// false = trial will immediately advance when response is recorded
trials[i].response_ends_trial = (typeof params.response_ends_trial === 'undefined') ? true : params.response_ends_trial;
// timing parameters
var default_timing_array = [];
for(var j = 0; j < params.stimuli[i].length; j++){
default_timing_array.push(1000);
}
trials[i].timing_stim = params.timing_stim || default_timing_array;
trials[i].timing_response = params.timing_response || -1; // if -1, then wait for response forever
// optional parameters
trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
trials[i].prompt = (typeof params.prompt === 'undefined') ? "" : params.prompt;
}
return trials;
};
for (var i = 0; i < trials.length; i++) {
trials[i] = {};
trials[i].stimuli = params.stimuli[i];
trials[i].choices = params.choices;
// option to show image for fixed time interval, ignoring key responses
// true = image will keep displaying after response
// false = trial will immediately advance when response is recorded
trials[i].response_ends_trial = (typeof params.response_ends_trial === 'undefined') ? true : params.response_ends_trial;
// timing parameters
var default_timing_array = [];
for (var j = 0; j < params.stimuli[i].length; j++) {
default_timing_array.push(1000);
}
trials[i].timing_stim = params.timing_stim || default_timing_array;
trials[i].timing_response = params.timing_response || -1; // if -1, then wait for response forever
// optional parameters
trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
trials[i].prompt = (typeof params.prompt === 'undefined') ? "" : params.prompt;
}
return trials;
};
plugin.trial = function(display_element, trial) {
plugin.trial = function(display_element, trial) {
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
// this array holds handlers from setTimeout calls
// that need to be cleared if the trial ends early
var setTimeoutHandlers = [];
// this array holds handlers from setTimeout calls
// that need to be cleared if the trial ends early
var setTimeoutHandlers = [];
// array to store if we have gotten a valid response for
// all the different response types
var validResponses = [];
for(var i = 0; i < trial.choices.length; i++){
validResponses[i] = false;
}
// array to store if we have gotten a valid response for
// all the different response types
var validResponses = [];
for (var i = 0; i < trial.choices.length; i++) {
validResponses[i] = false;
}
// array for response times for each of the different response types
var responseTimes = [];
for(var i = 0; i < trial.choices.length; i++){
responseTimes[i] = -1;
}
// array for response times for each of the different response types
var responseTimes = [];
for (var i = 0; i < trial.choices.length; i++) {
responseTimes[i] = -1;
}
// array for response keys for each of the different response types
var responseKeys = [];
for(var i = 0; i < trial.choices.length; i++){
responseKeys[i] = -1;
}
// array for response keys for each of the different response types
var responseKeys = [];
for (var i = 0; i < trial.choices.length; i++) {
responseKeys[i] = -1;
}
// function to check if all of the valid responses are received
function checkAllResponsesAreValid(){
for(var i = 0; i<validResponses.length; i++){
if(validResponses[i] == false){
return false;
}
}
return true;
}
// function to check if all of the valid responses are received
function checkAllResponsesAreValid() {
for (var i = 0; i < validResponses.length; i++) {
if (validResponses[i] == false) {
return false;
}
}
return true;
}
// function to end trial when it is time
var end_trial = function() {
// function to end trial when it is time
var end_trial = function() {
// kill any remaining setTimeout handlers
for (var i = 0; i < setTimeoutHandlers.length; i++) {
clearTimeout(setTimeoutHandlers[i]);
}
// kill any remaining setTimeout handlers
for (var i = 0; i < setTimeoutHandlers.length; i++) {
clearTimeout(setTimeoutHandlers[i]);
}
// kill keyboard listeners
jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
// kill keyboard listeners
jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
// gather the data to store for the trial
var trial_data = {
"rt": JSON.stringify(responseTimes),
"stimulus": JSON.stringify(trial.stimuli),
"key_press": JSON.stringify(responseKeys)
};
// gather the data to store for the trial
var trial_data = {
"rt": JSON.stringify(responseTimes),
"stimulus": JSON.stringify(trial.stimuli),
"key_press": JSON.stringify(responseKeys)
};
jsPsych.data.write(trial_data);
jsPsych.data.write(trial_data);
// clear the display
display_element.html('');
// clear the display
display_element.html('');
// move on to the next trial
jsPsych.finishTrial();
};
// move on to the next trial
jsPsych.finishTrial();
};
// function to handle responses by the subject
var after_response = function(info) {
// function to handle responses by the subject
var after_response = function(info) {
var whichResponse;
for(var i = 0; i<trial.choices.length; i++){
var whichResponse;
for (var i = 0; i < trial.choices.length; i++) {
for(var j = 0; j < trial.choices[i].length; j++){
keycode = (typeof trial.choices[i][j] == 'string') ? jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.choices[i][j]) : trial.choices[i][j];
if(info.key == keycode){
whichResponse = i;
break;
}
}
for (var j = 0; j < trial.choices[i].length; j++) {
keycode = (typeof trial.choices[i][j] == 'string') ? jsPsych.pluginAPI.convertKeyCharacterToKeyCode(trial.choices[i][j]) : trial.choices[i][j];
if (info.key == keycode) {
whichResponse = i;
break;
}
}
if(typeof whichResponse !== 'undefined') {
break;
}
}
if (typeof whichResponse !== 'undefined') {
break;
}
}
if(validResponses[whichResponse] != true){
validResponses[whichResponse] = true;
responseTimes[whichResponse] = info.rt;
responseKeys[whichResponse] = info.key;
}
if (validResponses[whichResponse] != true) {
validResponses[whichResponse] = true;
responseTimes[whichResponse] = info.rt;
responseKeys[whichResponse] = info.key;
}
if (trial.response_ends_trial) {
if (trial.response_ends_trial) {
if(checkAllResponsesAreValid()){
end_trial();
}
if (checkAllResponsesAreValid()) {
end_trial();
}
}
}
};
};
// flattened version of the choices array
var allchoices = [];
for(var i=0;i<trial.choices.length; i++){
allchoices = allchoices.concat(trial.choices[i]);
}
// flattened version of the choices array
var allchoices = [];
for (var i = 0; i < trial.choices.length; i++) {
allchoices = allchoices.concat(trial.choices[i]);
}
var whichStimulus = 0;
var whichStimulus = 0;
function showNextStimulus(){
function showNextStimulus() {
// display stimulus
if (!trial.is_html) {
display_element.append($('<img>', {
src: trial.stimuli[whichStimulus],
id: 'jspsych-multi-stim-multi-response-stimulus'
}));
} else {
display_element.append($('<div>', {
html: trial.stimuli[whichStimulus],
id: 'jspsych-multi-stim-multi-response-stimulus'
}));
}
// display stimulus
if (!trial.is_html) {
display_element.append($('<img>', {
src: trial.stimuli[whichStimulus],
id: 'jspsych-multi-stim-multi-response-stimulus'
}));
} else {
display_element.append($('<div>', {
html: trial.stimuli[whichStimulus],
id: 'jspsych-multi-stim-multi-response-stimulus'
}));
}
//show prompt if there is one
if (trial.prompt !== "") {
display_element.append(trial.prompt);
}
//show prompt if there is one
if (trial.prompt !== "") {
display_element.append(trial.prompt);
}
if (typeof trial.timing_stim[whichStimulus] !== 'undefined' && trial.timing_stim[whichStimulus] > 0) {
var t1 = setTimeout(function() {
// clear the display, or hide the display
if(typeof trial.stimuli[whichStimulus + 1] !== 'undefined'){
display_element.html('');
// show the next stimulus
whichStimulus++;
showNextStimulus();
} else {
$('#jspsych-multi-stim-multi-response-stimulus').css('visibility','hidden');
}
if (typeof trial.timing_stim[whichStimulus] !== 'undefined' && trial.timing_stim[whichStimulus] > 0) {
var t1 = setTimeout(function() {
// clear the display, or hide the display
if (typeof trial.stimuli[whichStimulus + 1] !== 'undefined') {
display_element.html('');
// show the next stimulus
whichStimulus++;
showNextStimulus();
} else {
$('#jspsych-multi-stim-multi-response-stimulus').css('visibility', 'hidden');
}
}, trial.timing_stim[whichStimulus]);
}, trial.timing_stim[whichStimulus]);
setTimeoutHandlers.push(t1);
}
setTimeoutHandlers.push(t1);
}
}
}
// show first stimulus
showNextStimulus();
// show first stimulus
showNextStimulus();
// start the response listener
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse(after_response, allchoices, "date", true);
// start the response listener
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: allchoices,
rt_method: 'date',
persist: true,
allow_held_key: false
});
// end trial if time limit is set
if (trial.timing_response > 0) {
var t2 = setTimeout(function() {
end_trial();
}, trial.timing_response);
setTimeoutHandlers.push(t2);
}
// end trial if time limit is set
if (trial.timing_response > 0) {
var t2 = setTimeout(function() {
end_trial();
}, trial.timing_response);
setTimeoutHandlers.push(t2);
}
};
};
return plugin;
})();
return plugin;
})();
})(jQuery);

View File

@ -64,8 +64,13 @@
}
// listen for responses
var key_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.key_increase, trial.key_decrease], 'date', true);
var key_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: [trial.key_increase, trial.key_decrease],
rt_method: 'date',
persist: true,
allow_held_key: true
});
// draw first iteration
draw(param);

View File

@ -132,7 +132,13 @@
jsPsych.finishTrial();
}
jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.same_key, trial.different_key], 'date', false);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: [trial.same_key, trial.different_key],
rt_method: 'date',
persist: false,
allow_held_key: false
});
}

View File

@ -111,8 +111,15 @@
};
// start the response listener
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'audio', false, context, startTime);
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'audio',
persist: false,
allow_held_key: false,
audio_context: context,
audio_context_start_time: startTime
});
// end trial if time limit is set
if (trial.timing_response > 0) {
var t2 = setTimeout(function() {

View File

@ -115,7 +115,13 @@
// start the response listener
if(JSON.stringify(trial.choices) != JSON.stringify(["none"])) {
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices);
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'date',
persist: false,
allow_held_key: false
});
}
// hide image if timing is set

View File

@ -19,9 +19,19 @@
var trials = [];
for (var i = 0; i < params.questions.length; i++) {
var rows = [], cols = [];
if(typeof params.rows == 'undefined' || typeof params.columns == 'undefined'){
for(var j = 0; j < params.questions[i].length; j++){
cols.push(40);
rows.push(1);
}
}
trials.push({
preamble: typeof params.preamble == 'undefined' ? "" : params.preamble[i],
questions: params.questions[i]
questions: params.questions[i],
rows: typeof params.rows == 'undefined' ? rows : params.rows[i],
columns: typeof params.columns == 'undefined' ? cols : params.columns[i]
});
}
return trials;
@ -54,7 +64,7 @@
$("#jspsych-survey-text-" + i).append('<p class="jspsych-survey-text">' + trial.questions[i] + '</p>');
// add text box
$("#jspsych-survey-text-" + i).append('<input type="text" name="#jspsych-survey-text-response-' + i + '"></input>');
$("#jspsych-survey-text-" + i).append('<textarea name="#jspsych-survey-text-response-' + i + '" cols="'+trial.columns[i]+'" rows="'+trial.rows[i]+'"></textarea>');
}
// add submit button
@ -72,7 +82,7 @@
var question_data = {};
$("div.jspsych-survey-text-question").each(function(index) {
var id = "Q" + index;
var val = $(this).children('input').val();
var val = $(this).children('textarea').val();
var obje = {};
obje[id] = val;
$.extend(question_data, obje);

View File

@ -62,7 +62,13 @@
display_element.click(mouse_listener);
var start_time = (new Date()).getTime();
} else {
jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.cont_key);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.cont_key,
rt_method: 'date',
persist: false,
allow_held_key: false
});
}
function save_data(key, rt) {

View File

@ -129,7 +129,13 @@
var valid_keys = [trial.target_present_key, trial.target_absent_key];
key_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, valid_keys, 'date', false);
key_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: valid_keys,
rt_method: 'date',
persist: false,
allow_held_key: false
});
if (trial.timing_max_search > -1) {
@ -171,7 +177,7 @@
}
function clear_display() {
paper.clear();
display_element.html('');
}
}

View File

@ -125,7 +125,13 @@
});
}
key_listener = jsPsych.pluginAPI.getKeyboardResponse(after_response, trial.choices, 'date', true);
key_listener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'date',
persist: true,
allow_held_key: false
});
if (trial.timing_pre_movement > 0) {
setTimeout(function() {

View File

@ -9,172 +9,177 @@
*/
(function($) {
jsPsych.xab = (function() {
jsPsych.xab = (function() {
var plugin = {};
var plugin = {};
plugin.create = function(params) {
plugin.create = function(params) {
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
params = jsPsych.pluginAPI.enforceArray(params, ['data']);
// the number of trials is determined by how many entries the params.stimuli array has
var trials = new Array(params.stimuli.length);
// the number of trials is determined by how many entries the params.stimuli array has
var trials = new Array(params.stimuli.length);
for (var i = 0; i < trials.length; i++) {
trials[i] = {};
trials[i].x_path = params.stimuli[i][0];
// if there is only a pair of stimuli, then the first is the target and is shown twice.
// if there is a triplet, then the first is X, the second is the target, and the third is foil (useful for non-exact-match XAB).
if (params.stimuli[i].length == 2) {
trials[i].a_path = params.stimuli[i][0];
trials[i].b_path = params.stimuli[i][1];
} else {
trials[i].a_path = params.stimuli[i][1];
trials[i].b_path = params.stimuli[i][2];
}
trials[i].left_key = params.left_key || 81; // defaults to 'q'
trials[i].right_key = params.right_key || 80; // defaults to 'p'
// timing parameters
trials[i].timing_x = params.timing_x || 1000; // defaults to 1000msec.
trials[i].timing_xab_gap = params.timing_xab_gap || 1000; // defaults to 1000msec.
trials[i].timing_ab = params.timing_ab || -1; // defaults to -1, meaning infinite time on AB. If a positive number is used, then AB will only be displayed for that length.
// optional parameters
trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
trials[i].prompt = (typeof params.prompt === 'undefined') ? "" : params.prompt;
for (var i = 0; i < trials.length; i++) {
trials[i] = {};
trials[i].x_path = params.stimuli[i][0];
// if there is only a pair of stimuli, then the first is the target and is shown twice.
// if there is a triplet, then the first is X, the second is the target, and the third is foil (useful for non-exact-match XAB).
if (params.stimuli[i].length == 2) {
trials[i].a_path = params.stimuli[i][0];
trials[i].b_path = params.stimuli[i][1];
} else {
trials[i].a_path = params.stimuli[i][1];
trials[i].b_path = params.stimuli[i][2];
}
trials[i].left_key = params.left_key || 81; // defaults to 'q'
trials[i].right_key = params.right_key || 80; // defaults to 'p'
// timing parameters
trials[i].timing_x = params.timing_x || 1000; // defaults to 1000msec.
trials[i].timing_xab_gap = params.timing_xab_gap || 1000; // defaults to 1000msec.
trials[i].timing_ab = params.timing_ab || -1; // defaults to -1, meaning infinite time on AB. If a positive number is used, then AB will only be displayed for that length.
// optional parameters
trials[i].is_html = (typeof params.is_html === 'undefined') ? false : params.is_html;
trials[i].prompt = (typeof params.prompt === 'undefined') ? "" : params.prompt;
}
return trials;
};
}
return trials;
};
plugin.trial = function(display_element, trial) {
plugin.trial = function(display_element, trial) {
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
// if any trial variables are functions
// this evaluates the function and replaces
// it with the output of the function
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
// this array holds handlers from setTimeout calls
// that need to be cleared if the trial ends early
var setTimeoutHandlers = [];
// this array holds handlers from setTimeout calls
// that need to be cleared if the trial ends early
var setTimeoutHandlers = [];
// how we display the content depends on whether the content is
// HTML code or an image path.
if (!trial.is_html) {
display_element.append($('<img>', {
src: trial.x_path,
"class": 'jspsych-xab-stimulus'
}));
} else {
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus',
html: trial.x_path
}));
}
// how we display the content depends on whether the content is
// HTML code or an image path.
if (!trial.is_html) {
display_element.append($('<img>', {
src: trial.x_path,
"class": 'jspsych-xab-stimulus'
}));
} else {
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus',
html: trial.x_path
}));
}
// start a timer of length trial.timing_x to move to the next part of the trial
setTimeout(function() {
showBlankScreen();
}, trial.timing_x);
// start a timer of length trial.timing_x to move to the next part of the trial
setTimeout(function() {
showBlankScreen();
}, trial.timing_x);
function showBlankScreen() {
// remove the x stimulus
$('.jspsych-xab-stimulus').remove();
function showBlankScreen() {
// remove the x stimulus
$('.jspsych-xab-stimulus').remove();
// start timer
setTimeout(function() {
showSecondStimulus();
}, trial.timing_xab_gap);
}
// start timer
setTimeout(function() {
showSecondStimulus();
}, trial.timing_xab_gap);
}
function showSecondStimulus() {
function showSecondStimulus() {
// randomize whether the target is on the left or the right
var images = [trial.a_path, trial.b_path];
var target_left = (Math.floor(Math.random() * 2) === 0); // 50% chance target is on left.
if (!target_left) {
images = [trial.b_path, trial.a_path];
}
// randomize whether the target is on the left or the right
var images = [trial.a_path, trial.b_path];
var target_left = (Math.floor(Math.random() * 2) === 0); // 50% chance target is on left.
if (!target_left) {
images = [trial.b_path, trial.a_path];
}
// show the options
if (!trial.is_html) {
display_element.append($('<img>', {
"src": images[0],
"class": 'jspsych-xab-stimulus left'
}));
display_element.append($('<img>', {
"src": images[1],
"class": 'jspsych-xab-stimulus right'
}));
} else {
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus left',
html: images[0]
}));
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus right',
html: images[1]
}));
}
// show the options
if (!trial.is_html) {
display_element.append($('<img>', {
"src": images[0],
"class": 'jspsych-xab-stimulus left'
}));
display_element.append($('<img>', {
"src": images[1],
"class": 'jspsych-xab-stimulus right'
}));
} else {
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus left',
html: images[0]
}));
display_element.append($('<div>', {
"class": 'jspsych-xab-stimulus right',
html: images[1]
}));
}
if (trial.prompt !== "") {
display_element.append(trial.prompt);
}
if (trial.prompt !== "") {
display_element.append(trial.prompt);
}
// if timing_ab is > 0, then we hide the stimuli after timing_ab milliseconds
if (trial.timing_ab > 0) {
setTimeoutHandlers.push(setTimeout(function() {
$('.jspsych-xab-stimulus').css('visibility', 'hidden');
}, trial.timing_ab));
}
// if timing_ab is > 0, then we hide the stimuli after timing_ab milliseconds
if (trial.timing_ab > 0) {
setTimeoutHandlers.push(setTimeout(function() {
$('.jspsych-xab-stimulus').css('visibility', 'hidden');
}, trial.timing_ab));
}
// create the function that triggers when a key is pressed.
var after_response = function(info) {
// create the function that triggers when a key is pressed.
var after_response = function(info) {
// kill any remaining setTimeout handlers
for (var i = 0; i < setTimeoutHandlers.length; i++) {
clearTimeout(setTimeoutHandlers[i]);
}
// kill any remaining setTimeout handlers
for (var i = 0; i < setTimeoutHandlers.length; i++) {
clearTimeout(setTimeoutHandlers[i]);
}
var correct = false; // true when the correct response is chosen
var correct = false; // true when the correct response is chosen
if (info.key == trial.left_key) // 'q' key by default
{
if (target_left) {
correct = true;
}
} else if (info.key == trial.right_key) // 'p' key by default
{
if (!target_left) {
correct = true;
}
}
if (info.key == trial.left_key) // 'q' key by default
{
if (target_left) {
correct = true;
}
} else if (info.key == trial.right_key) // 'p' key by default
{
if (!target_left) {
correct = true;
}
}
// create object to store data from trial
var trial_data = {
"rt": info.rt,
"correct": correct,
"stimulus": JSON.stringify([trial.x_path,trial.a_path,trial.b_path]),
"key_press": info.key
};
jsPsych.data.write(trial_data);
// create object to store data from trial
var trial_data = {
"rt": info.rt,
"correct": correct,
"stimulus": JSON.stringify([trial.x_path, trial.a_path, trial.b_path]),
"key_press": info.key
};
jsPsych.data.write(trial_data);
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
jsPsych.finishTrial();
// move on to the next trial after timing_post_trial milliseconds
jsPsych.finishTrial();
};
};
jsPsych.pluginAPI.getKeyboardResponse(after_response, [trial.left_key, trial.right_key], 'date', false);
jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: [trial.left_key, trial.right_key],
rt_method: 'date',
persist: false,
allow_held_key: false
});
}
};
}
};
return plugin;
})();
return plugin;
})();
})(jQuery);

View File

@ -0,0 +1,40 @@
<!doctype html>
<html>
<head>
<script src="js/jquery.min.js"></script>
<script src="../jspsych.js"></script>
<script src="../plugins/jspsych-single-stim.js"></script>
<link rel="stylesheet" href="../css/jspsych.css"></link>
<style>
img { width: 300px; }
</style>
</head>
<body>
<div id="jspsych-target"></div>
</body>
<script>
var block_1 = {
type: 'single-stim',
stimuli: ['img/happy_face_1.jpg', 'img/sad_face_1.jpg'],
choices: [89,78], // Y or N
prompt: '<p class="center-content">Have you seen this face before? Y or N.</p>',
on_finish: function(){
jsPsych.data.addDataToLastTrial({correct: true});
}
}
function start(){
jsPsych.init({
display_element: $('#jspsych-target'),
experiment_structure: [block_1],
on_finish: function(){
jsPsych.data.displayData();
}
});
}
jsPsych.preloadImages(['img/happy_face_1.jpg','img/sad_face_1.jpg'], start);
</script>
</html>

View File

@ -23,6 +23,9 @@
correct_text: "<p class='center-content'>Correct. This face is %ANS%.</p>",
incorrect_text: "<p class='center-content'>Incorrect. This face is %ANS%.</p>",
prompt: "<p class='center-content'>Press H if the face is happy. Press S if the face is sad.</p>",
timing_response: 1500,
show_feedback_on_timeout: false,
force_correct_button_press: true
};
function start(){

View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<script src="js/jquery.min.js"></script>
<script src="../jspsych.js"></script>
<link rel="stylesheet" href="../css/jspsych.css"></link>
<style>
img { width: 300px; }
</style>
</head>
<body>
<div id="jspsych-target"></div>
</body>
<script>
var repeated_list = jsPsych.randomization.repeat([1,2,3,4,5],3);
console.log(JSON.stringify(repeated_list));
var no_neighbor_repeats = jsPsych.randomization.shuffleNoRepeats(repeated_list);
console.log(JSON.stringify(no_neighbor_repeats));
</script>
</html>