mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-12 08:38:11 +00:00
Merge pull request #1494 from jspsych/feature-timeline-events
merge timeline events into all timeline vars branch
This commit is contained in:
commit
0272250bb4
60
jspsych.js
60
jspsych.js
@ -505,10 +505,11 @@ window.jsPsych = (function() {
|
||||
// if node has not started yet (progress.current_location == -1),
|
||||
// then try to start the node.
|
||||
if (progress.current_location == -1) {
|
||||
// check for conditonal function on nodes with timelines
|
||||
// check for on_timeline_start and conditonal function on nodes with timelines
|
||||
if (typeof timeline_parameters != 'undefined') {
|
||||
if (typeof timeline_parameters.conditional_function !== 'undefined') {
|
||||
jsPsych.internal.call_immediate = true;
|
||||
// only run the conditional function if this is the first repetition of the timeline when
|
||||
// repetitions > 1, and only when on the first variable set
|
||||
if (typeof timeline_parameters.conditional_function !== 'undefined' && progress.current_repetition==0 && progress.current_variable_set == 0) {
|
||||
var conditional_result = timeline_parameters.conditional_function();
|
||||
jsPsych.internal.call_immediate = false;
|
||||
// if the conditional_function() returns false, then the timeline
|
||||
@ -517,17 +518,23 @@ window.jsPsych = (function() {
|
||||
progress.done = true;
|
||||
return true;
|
||||
}
|
||||
// if the conditonal_function() returns true, then the node can start
|
||||
else {
|
||||
progress.current_location = 0;
|
||||
// // if the conditonal_function() returns true, then the node can start
|
||||
// else {
|
||||
// progress.current_location = 0;
|
||||
// }
|
||||
}
|
||||
// if we reach this point then the node has its own timeline and will start
|
||||
// so we need to check if there is an on_timeline_start function
|
||||
if (typeof timeline_parameters.on_timeline_start !== 'undefined'){
|
||||
timeline_parameters.on_timeline_start();
|
||||
}
|
||||
// if there is no conditional_function, then the node can start
|
||||
else {
|
||||
progress.current_location = 0;
|
||||
// // if there is no conditional_function, then the node can start
|
||||
// else {
|
||||
// progress.current_location = 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
// if the node does not have a timeline, then it can start
|
||||
// if we reach this point, then either the node doesn't have a timeline of the
|
||||
// conditional function returned true and it can start
|
||||
progress.current_location = 0;
|
||||
// call advance again on this node now that it is pointing to a new location
|
||||
return this.advance();
|
||||
@ -552,6 +559,7 @@ window.jsPsych = (function() {
|
||||
}
|
||||
|
||||
// if we've reached the end of the timeline (which, if the code is here, we have)
|
||||
|
||||
// there are a few steps to see what to do next...
|
||||
|
||||
// first, check the timeline_variables to see if we need to loop through again
|
||||
@ -566,11 +574,23 @@ window.jsPsych = (function() {
|
||||
// if we're all done with the timeline_variables, then check to see if there are more repetitions
|
||||
else if (progress.current_repetition < timeline_parameters.repetitions - 1) {
|
||||
this.nextRepetiton();
|
||||
// check to see if there is an on_timeline_finish function
|
||||
if (typeof timeline_parameters.on_timeline_finish !== 'undefined'){
|
||||
timeline_parameters.on_timeline_finish();
|
||||
}
|
||||
return this.advance();
|
||||
}
|
||||
|
||||
|
||||
// if we're all done with the repetitions...
|
||||
else {
|
||||
// check to see if there is an on_timeline_finish function
|
||||
if (typeof timeline_parameters.on_timeline_finish !== 'undefined'){
|
||||
timeline_parameters.on_timeline_finish();
|
||||
}
|
||||
|
||||
// if we're all done with the repetitions, check if there is a loop function.
|
||||
else if (typeof timeline_parameters.loop_function !== 'undefined') {
|
||||
if (typeof timeline_parameters.loop_function !== 'undefined') {
|
||||
jsPsych.internal.call_immediate = true;
|
||||
if (timeline_parameters.loop_function(this.generatedData())) {
|
||||
this.reset();
|
||||
@ -583,12 +603,12 @@ window.jsPsych = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
// no more loops on this timeline, we're done!
|
||||
else {
|
||||
progress.done = true;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// no more loops on this timeline, we're done!
|
||||
progress.done = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -818,12 +838,16 @@ window.jsPsych = (function() {
|
||||
sample: parameters.sample,
|
||||
randomize_order: typeof parameters.randomize_order == 'undefined' ? false : parameters.randomize_order,
|
||||
repetitions: typeof parameters.repetitions == 'undefined' ? 1 : parameters.repetitions,
|
||||
timeline_variables: typeof parameters.timeline_variables == 'undefined' ? [{}] : parameters.timeline_variables
|
||||
timeline_variables: typeof parameters.timeline_variables == 'undefined' ? [{}] : parameters.timeline_variables,
|
||||
on_timeline_finish: parameters.on_timeline_finish,
|
||||
on_timeline_start: parameters.on_timeline_start,
|
||||
};
|
||||
|
||||
self.setTimelineVariablesOrder();
|
||||
|
||||
// extract all of the node level data and parameters
|
||||
// but remove all of the timeline-level specific information
|
||||
// since this will be used to copy things down hierarchically
|
||||
var node_data = Object.assign({}, parameters);
|
||||
delete node_data.timeline;
|
||||
delete node_data.conditional_function;
|
||||
@ -832,6 +856,8 @@ window.jsPsych = (function() {
|
||||
delete node_data.repetitions;
|
||||
delete node_data.timeline_variables;
|
||||
delete node_data.sample;
|
||||
delete node_data.on_timeline_start;
|
||||
delete node_data.on_timeline_finish;
|
||||
node_trial_data = node_data; // store for later...
|
||||
|
||||
// create a TimelineNode for each element in the timeline
|
||||
|
@ -367,3 +367,175 @@ describe('on_trial_start', function(){
|
||||
utils.pressKey('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on_timeline_finish', function(){
|
||||
test('should fire once when timeline is complete', function(){
|
||||
|
||||
var on_finish_fn = jest.fn();
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
},
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
},
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_finish: on_finish_fn
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
utils.pressKey(32);
|
||||
expect(on_finish_fn).not.toHaveBeenCalled();
|
||||
utils.pressKey(32);
|
||||
expect(on_finish_fn).not.toHaveBeenCalled();
|
||||
utils.pressKey(32);
|
||||
expect(on_finish_fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should fire once even with timeline variables', function(){
|
||||
|
||||
var on_finish_fn = jest.fn();
|
||||
|
||||
var tvs = [{
|
||||
x: 1,
|
||||
x: 2,
|
||||
}]
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_finish: on_finish_fn,
|
||||
timeline_variables: tvs
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
expect(on_finish_fn.mock.calls.length).toBe(1);
|
||||
|
||||
})
|
||||
|
||||
test('should fire on every repetition', function(){
|
||||
|
||||
var on_finish_fn = jest.fn();
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_finish: on_finish_fn,
|
||||
repetitions: 2
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
expect(on_finish_fn.mock.calls.length).toBe(2);
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('on_timeline_start', function(){
|
||||
test('should fire once when timeline starts', function(){
|
||||
|
||||
var on_start_fn = jest.fn();
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
},
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
},
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_start: on_start_fn
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
expect(on_start_fn).toHaveBeenCalled();
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
expect(on_start_fn.mock.calls.length).toBe(1);
|
||||
|
||||
})
|
||||
|
||||
test('should fire once even with timeline variables', function(){
|
||||
|
||||
var on_start_fn = jest.fn();
|
||||
|
||||
var tvs = [{
|
||||
x: 1,
|
||||
x: 2,
|
||||
}]
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_start: on_start_fn,
|
||||
timeline_variables: tvs
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
expect(on_start_fn).toHaveBeenCalled();
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
expect(on_start_fn.mock.calls.length).toBe(1);
|
||||
|
||||
})
|
||||
|
||||
test('should fire on every repetition', function(){
|
||||
|
||||
var on_start_fn = jest.fn();
|
||||
|
||||
var mini_timeline = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}
|
||||
],
|
||||
on_timeline_start: on_start_fn,
|
||||
repetitions: 2
|
||||
}
|
||||
|
||||
jsPsych.init({timeline: [mini_timeline]});
|
||||
|
||||
expect(on_start_fn).toHaveBeenCalled();
|
||||
utils.pressKey(32);
|
||||
utils.pressKey(32);
|
||||
expect(on_start_fn.mock.calls.length).toBe(2);
|
||||
|
||||
})
|
||||
})
|
@ -166,6 +166,36 @@ describe('loop function', function(){
|
||||
utils.pressKey('a');
|
||||
});
|
||||
|
||||
test('only runs once when timeline variables are used', function(){
|
||||
var count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}],
|
||||
timeline_variables:[{a:1},{a:2}],
|
||||
loop_function: function(){
|
||||
count++;
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
// first trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(count).toBe(0);
|
||||
|
||||
// second trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(count).toBe(1);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('conditional function', function(){
|
||||
@ -269,6 +299,73 @@ describe('conditional function', function(){
|
||||
expect(conditional_count).toBe(2);
|
||||
});
|
||||
|
||||
test('executes only once even when repetitions is > 1', function(){
|
||||
var conditional_count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}],
|
||||
repetitions: 2,
|
||||
conditional_function: function(){
|
||||
conditional_count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
|
||||
// first trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
|
||||
// second trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
})
|
||||
|
||||
test('executes only once when timeline variables are used', function(){
|
||||
var conditional_count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'html-keyboard-response',
|
||||
stimulus: 'foo'
|
||||
}],
|
||||
timeline_variables: [
|
||||
{a:1},
|
||||
{a:2}
|
||||
],
|
||||
conditional_function: function(){
|
||||
conditional_count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
|
||||
// first trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
|
||||
// second trial
|
||||
utils.pressKey(32);
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
})
|
||||
|
||||
test('timeline variables from nested timelines are available', function(){
|
||||
var trial = {
|
||||
type: 'html-keyboard-response',
|
||||
|
Loading…
Reference in New Issue
Block a user