mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 18:50:54 +00:00
verbump and proper test_experiment.js ignore
This commit is contained in:
parent
b43a16ac1e
commit
2788379a6f
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "psychojs",
|
"name": "psychojs",
|
||||||
"version": "2024.1.0",
|
"version": "2024.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Helps run in-browser neuroscience, psychology, and psychophysics experiments",
|
"description": "Helps run in-browser neuroscience, psychology, and psychophysics experiments",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -1,510 +0,0 @@
|
|||||||
/**************
|
|
||||||
* Gabor Test *
|
|
||||||
**************/
|
|
||||||
|
|
||||||
// import { core, data, sound, util, visual } from '../../psychojs_experimental/psychojsPR519.js';
|
|
||||||
// import { core, data, sound, util, visual } from "../out/psychojs-2024.1.0.js";
|
|
||||||
import { core, data, sound, util, visual } from "./index.js";
|
|
||||||
|
|
||||||
// import {StimInspector} from 'https://run.pavlovia.org/lgtst/stiminspector/StimInspector.js';
|
|
||||||
// import {StimInspector} from '../stiminspector/StimInspector.js';
|
|
||||||
// import {PsyexpReader} from '../psyexpreader/PsyexpReader.js';
|
|
||||||
const { PsychoJS } = core;
|
|
||||||
const { TrialHandler } = data;
|
|
||||||
const { Scheduler } = util;
|
|
||||||
//some handy aliases as in the psychopy scripts;
|
|
||||||
const { abs, sin, cos, PI: pi, sqrt } = Math;
|
|
||||||
const { round } = util;
|
|
||||||
|
|
||||||
// store info about the experiment session:
|
|
||||||
let expName = 'gabor'; // from the Builder filename that created this script
|
|
||||||
let expInfo = {};
|
|
||||||
|
|
||||||
// Start code blocks for 'Before Experiment'
|
|
||||||
// init psychoJS:
|
|
||||||
const psychoJS = new PsychoJS({
|
|
||||||
debug: true
|
|
||||||
});
|
|
||||||
window.psychoJS = psychoJS;
|
|
||||||
window.util = util;
|
|
||||||
|
|
||||||
// open window:
|
|
||||||
psychoJS.openWindow({
|
|
||||||
fullscr: false,
|
|
||||||
color: new util.Color("gray"),
|
|
||||||
units: 'height',
|
|
||||||
waitBlanking: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// new StimInspector(psychoJS.window, { core, data, sound, util, visual });
|
|
||||||
|
|
||||||
// schedule the experiment:
|
|
||||||
psychoJS.schedule(psychoJS.gui.DlgFromDict({
|
|
||||||
dictionary: expInfo,
|
|
||||||
title: expName
|
|
||||||
}));
|
|
||||||
|
|
||||||
const flowScheduler = new Scheduler(psychoJS);
|
|
||||||
const dialogCancelScheduler = new Scheduler(psychoJS);
|
|
||||||
psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === 'OK'); }, flowScheduler, dialogCancelScheduler);
|
|
||||||
|
|
||||||
// flowScheduler gets run if the participants presses OK
|
|
||||||
flowScheduler.add(updateInfo); // add timeStamp
|
|
||||||
flowScheduler.add(experimentInit);
|
|
||||||
// flowScheduler.add(instructRoutineBegin());
|
|
||||||
// flowScheduler.add(instructRoutineEachFrame());
|
|
||||||
// flowScheduler.add(instructRoutineEnd());
|
|
||||||
flowScheduler.add(gaborRoutineBegin());
|
|
||||||
flowScheduler.add(gaborRoutineEachFrame());
|
|
||||||
flowScheduler.add(gaborRoutineEnd());
|
|
||||||
flowScheduler.add(quitPsychoJS, '', true);
|
|
||||||
|
|
||||||
// quit if user presses Cancel in dialog box:
|
|
||||||
dialogCancelScheduler.add(quitPsychoJS, '', false);
|
|
||||||
|
|
||||||
psychoJS.start({
|
|
||||||
expName: expName,
|
|
||||||
expInfo: expInfo,
|
|
||||||
configURL: "../config.json",
|
|
||||||
resources: [
|
|
||||||
{
|
|
||||||
name: "starformation.jpg",
|
|
||||||
path: "./test_resources/starformation.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cool.gif",
|
|
||||||
path: "./test_resources/cool.gif"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delorean.gif",
|
|
||||||
path: "./test_resources/delorean.gif"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "silverhand.gif",
|
|
||||||
path: "./test_resources/silverhand.gif"
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: "007",
|
|
||||||
// path: "007.jpg"
|
|
||||||
// },
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
psychoJS.experimentLogger.setLevel(core.Logger.ServerLevel.WARNING);
|
|
||||||
|
|
||||||
var frameDur;
|
|
||||||
async function updateInfo() {
|
|
||||||
expInfo['date'] = util.MonotonicClock.getDateStr(); // add a simple timestamp
|
|
||||||
expInfo['expName'] = expName;
|
|
||||||
expInfo['psychopyVersion'] = '2021.3.0';
|
|
||||||
expInfo['OS'] = window.navigator.platform;
|
|
||||||
|
|
||||||
// store frame rate of monitor if we can measure it successfully
|
|
||||||
expInfo['frameRate'] = psychoJS.window.getActualFrameRate();
|
|
||||||
if (typeof expInfo['frameRate'] !== 'undefined')
|
|
||||||
frameDur = 1.0 / Math.round(expInfo['frameRate']);
|
|
||||||
else
|
|
||||||
frameDur = 1.0 / 60.0; // couldn't get a reliable measure so guess
|
|
||||||
|
|
||||||
// add info from the URL:
|
|
||||||
util.addInfoFromUrl(expInfo);
|
|
||||||
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
var instructClock;
|
|
||||||
var ready;
|
|
||||||
var gaborClock;
|
|
||||||
var gabor;
|
|
||||||
var stims = [];
|
|
||||||
window.grating2BlendMode = 'add';
|
|
||||||
var globalClock;
|
|
||||||
var routineTimer;
|
|
||||||
|
|
||||||
function addWheelListener () {
|
|
||||||
let v = 1.;
|
|
||||||
window.addEventListener('wheel', (e) => {
|
|
||||||
if (!psychoJS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
psychoJS._window._stimsContainer.position.y += e.deltaY * v;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// var video;
|
|
||||||
async function experimentInit() {
|
|
||||||
// Initialize components for Routine "instruct"
|
|
||||||
instructClock = new util.Clock();
|
|
||||||
ready = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
|
|
||||||
psychoJS.window.backgroundImage = "starformation.jpg";
|
|
||||||
|
|
||||||
// Initialize components for Routine "gabor"
|
|
||||||
gaborClock = new util.Clock();
|
|
||||||
|
|
||||||
stims.push(
|
|
||||||
// new visual.GratingStim({
|
|
||||||
// win : psychoJS.window,
|
|
||||||
// name: 'morph',
|
|
||||||
// tex: 'sin',
|
|
||||||
// mask: undefined,
|
|
||||||
// ori: 0,
|
|
||||||
// size: [256, 512],
|
|
||||||
// pos: [0, 0],
|
|
||||||
// units: "pix",
|
|
||||||
// depth: 0
|
|
||||||
// })
|
|
||||||
new visual.GifStim({
|
|
||||||
win : psychoJS.window,
|
|
||||||
name: 'morph',
|
|
||||||
image: "silverhand.gif",
|
|
||||||
mask: undefined,
|
|
||||||
ori: 0,
|
|
||||||
size: [512, 512],
|
|
||||||
pos: [0, 0],
|
|
||||||
units: "pix",
|
|
||||||
depth: 0
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
window.stims = stims;
|
|
||||||
// Create some handy timers
|
|
||||||
globalClock = new util.Clock(); // to track the time since experiment started
|
|
||||||
routineTimer = new util.CountdownTimer(); // to track time remaining of each (non-slip) routine
|
|
||||||
addWheelListener();
|
|
||||||
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var t;
|
|
||||||
var frameN;
|
|
||||||
var continueRoutine;
|
|
||||||
var gotValidClick;
|
|
||||||
var _ready_allKeys;
|
|
||||||
var instructComponents;
|
|
||||||
function instructRoutineBegin(snapshot) {
|
|
||||||
return async function () {
|
|
||||||
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
|
|
||||||
|
|
||||||
//------Prepare to start Routine 'instruct'-------
|
|
||||||
t = 0;
|
|
||||||
instructClock.reset(); // clock
|
|
||||||
frameN = -1;
|
|
||||||
continueRoutine = true; // until we're told otherwise
|
|
||||||
// update component parameters for each repeat
|
|
||||||
ready.keys = undefined;
|
|
||||||
ready.rt = undefined;
|
|
||||||
_ready_allKeys = [];
|
|
||||||
// keep track of which components have finished
|
|
||||||
instructComponents = [];
|
|
||||||
instructComponents.push(ready);
|
|
||||||
|
|
||||||
for (const thisComponent of instructComponents)
|
|
||||||
if ('status' in thisComponent)
|
|
||||||
thisComponent.status = PsychoJS.Status.NOT_STARTED;
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function instructRoutineEachFrame() {
|
|
||||||
return async function () {
|
|
||||||
//------Loop for each frame of Routine 'instruct'-------
|
|
||||||
// get current time
|
|
||||||
t = instructClock.getTime();
|
|
||||||
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
|
|
||||||
// update/draw components on each frame
|
|
||||||
|
|
||||||
// *ready* updates
|
|
||||||
if (t >= 0 && ready.status === PsychoJS.Status.NOT_STARTED) {
|
|
||||||
// keep track of start time/frame for later
|
|
||||||
ready.tStart = t; // (not accounting for frame time here)
|
|
||||||
ready.frameNStart = frameN; // exact frame index
|
|
||||||
|
|
||||||
// keyboard checking is just starting
|
|
||||||
psychoJS.window.callOnFlip(function() { ready.clock.reset(); }); // t=0 on next screen flip
|
|
||||||
psychoJS.window.callOnFlip(function() { ready.start(); }); // start on screen flip
|
|
||||||
psychoJS.window.callOnFlip(function() { ready.clearEvents(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ready.status === PsychoJS.Status.STARTED) {
|
|
||||||
let theseKeys = ready.getKeys({keyList: [], waitRelease: false});
|
|
||||||
_ready_allKeys = _ready_allKeys.concat(theseKeys);
|
|
||||||
if (_ready_allKeys.length > 0) {
|
|
||||||
ready.keys = _ready_allKeys[_ready_allKeys.length - 1].name; // just the last key pressed
|
|
||||||
ready.rt = _ready_allKeys[_ready_allKeys.length - 1].rt;
|
|
||||||
// a response ends the routine
|
|
||||||
continueRoutine = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for quit (typically the Esc key)
|
|
||||||
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
|
|
||||||
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the Routine should terminate
|
|
||||||
if (!continueRoutine) { // a component has requested a forced-end of Routine
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
continueRoutine = false; // reverts to True if at least one component still running
|
|
||||||
for (const thisComponent of instructComponents)
|
|
||||||
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
|
|
||||||
continueRoutine = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh the screen if continuing
|
|
||||||
if (continueRoutine) {
|
|
||||||
return Scheduler.Event.FLIP_REPEAT;
|
|
||||||
} else {
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function instructRoutineEnd() {
|
|
||||||
return async function () {
|
|
||||||
//------Ending Routine 'instruct'-------
|
|
||||||
for (const thisComponent of instructComponents) {
|
|
||||||
if (typeof thisComponent.setAutoDraw === 'function') {
|
|
||||||
thisComponent.setAutoDraw(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ready.stop();
|
|
||||||
// the Routine "instruct" was not non-slip safe, so reset the non-slip timer
|
|
||||||
routineTimer.reset();
|
|
||||||
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var gaborComponents;
|
|
||||||
function gaborRoutineBegin(snapshot) {
|
|
||||||
return async function () {
|
|
||||||
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
|
|
||||||
|
|
||||||
//------Prepare to start Routine 'instruct'-------
|
|
||||||
t = 0;
|
|
||||||
gaborClock.reset(); // clock
|
|
||||||
frameN = -1;
|
|
||||||
continueRoutine = true; // until we're told otherwise
|
|
||||||
// update component parameters for each repeat
|
|
||||||
ready.keys = undefined;
|
|
||||||
ready.rt = undefined;
|
|
||||||
_ready_allKeys = [];
|
|
||||||
// keep track of which components have finished
|
|
||||||
gaborComponents = [];
|
|
||||||
gaborComponents.push(ready);
|
|
||||||
gaborComponents = [...gaborComponents, ...stims];
|
|
||||||
|
|
||||||
|
|
||||||
for (const thisComponent of gaborComponents)
|
|
||||||
if ('status' in thisComponent)
|
|
||||||
thisComponent.status = PsychoJS.Status.NOT_STARTED;
|
|
||||||
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var secTimer = 0;
|
|
||||||
var prevTime = performance.now();
|
|
||||||
var dynamicDimension = 0;
|
|
||||||
var newSize = [512, 512];
|
|
||||||
var newPos = [0, 0];
|
|
||||||
var sizeTests = [-512, -256.1, -128, 128, 256.6, 512];
|
|
||||||
var positionTests = [-256, -256.1, 256, 256.1, 0];
|
|
||||||
var anchorTests = ["left", "topleft", "top", "topright", "right", "bottomright", "bottom", "bottomleft", "center"];
|
|
||||||
var sizeTestsProgress = 0;
|
|
||||||
var positionTestsProgress = 0;
|
|
||||||
var anchorTestsProgress = 0;
|
|
||||||
var continueAutoTest = true;
|
|
||||||
window.stopTest = function () {
|
|
||||||
continueAutoTest = false;
|
|
||||||
};
|
|
||||||
window.startTest = function () {
|
|
||||||
continueAutoTest = true;
|
|
||||||
};
|
|
||||||
function gaborRoutineEachFrame() {
|
|
||||||
return async function () {
|
|
||||||
//------Loop for each frame of Routine 'gabor'-------
|
|
||||||
// get current time
|
|
||||||
t = gaborClock.getTime();
|
|
||||||
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
|
|
||||||
|
|
||||||
let i;
|
|
||||||
for (i = 0; i < stims.length; i++) {
|
|
||||||
if (t >= 0. && stims[i].status === PsychoJS.Status.NOT_STARTED) {
|
|
||||||
stims[i].tStart = t;
|
|
||||||
stims[i].frameNStart = frameN;
|
|
||||||
stims[i].setAutoDraw(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// testing code
|
|
||||||
secTimer += performance.now() - prevTime;
|
|
||||||
prevTime = performance.now();
|
|
||||||
if (secTimer >= 1000 && continueAutoTest)
|
|
||||||
{
|
|
||||||
secTimer = 0;
|
|
||||||
|
|
||||||
if (sizeTestsProgress < sizeTests.length * 2)
|
|
||||||
{
|
|
||||||
i = sizeTestsProgress % sizeTests.length;
|
|
||||||
newSize[dynamicDimension] = sizeTests[i];
|
|
||||||
stims[0].setSize(newSize);
|
|
||||||
sizeTestsProgress++;
|
|
||||||
console.log("stim size set to", stims[0].getSize());
|
|
||||||
if (sizeTestsProgress % sizeTests.length === 0)
|
|
||||||
{
|
|
||||||
dynamicDimension = (dynamicDimension + 1) % 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (sizeTestsProgress < sizeTests.length * 3)
|
|
||||||
{
|
|
||||||
i = sizeTestsProgress % sizeTests.length;
|
|
||||||
newSize[0] = sizeTests[i];
|
|
||||||
newSize[1] = sizeTests[i];
|
|
||||||
stims[0].setSize(newSize);
|
|
||||||
sizeTestsProgress++;
|
|
||||||
console.log("stim size set to", stims[0].getSize());
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
sizeTestsProgress >= sizeTests.length * 3 &&
|
|
||||||
positionTestsProgress < positionTests.length * 2)
|
|
||||||
{
|
|
||||||
i = positionTestsProgress % positionTests.length;
|
|
||||||
newPos[dynamicDimension] = positionTests[i];
|
|
||||||
stims[0].setPos(newPos);
|
|
||||||
positionTestsProgress++;
|
|
||||||
console.log("stim pos set to", stims[0].getPos());
|
|
||||||
if (positionTestsProgress % positionTests.length === 0)
|
|
||||||
{
|
|
||||||
newPos[dynamicDimension] = 0;
|
|
||||||
dynamicDimension = (dynamicDimension + 1) % 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(
|
|
||||||
sizeTestsProgress >= sizeTests.length * 3 &&
|
|
||||||
positionTestsProgress >= positionTests.length * 2 &&
|
|
||||||
anchorTestsProgress < anchorTests.length)
|
|
||||||
{
|
|
||||||
i = anchorTestsProgress % anchorTests.length;
|
|
||||||
stims[0].setAnchor(anchorTests[i]);
|
|
||||||
anchorTestsProgress++;
|
|
||||||
console.log("anchor set to", anchorTests[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
sizeTestsProgress >= sizeTests.length * 3 &&
|
|
||||||
positionTestsProgress >= positionTests.length * 2 &&
|
|
||||||
anchorTestsProgress >= anchorTests.length)
|
|
||||||
{
|
|
||||||
sizeTestsProgress = 0;
|
|
||||||
positionTestsProgress = 0;
|
|
||||||
anchorTestsProgress = 0;
|
|
||||||
dynamicDimension = 0;
|
|
||||||
newPos[0] = 0;
|
|
||||||
newPos[1] = 0;
|
|
||||||
newSize[0] = 512;
|
|
||||||
newSize[1] = 512;
|
|
||||||
console.log("============== full reset ==============");
|
|
||||||
stims[0].setPos(newPos);
|
|
||||||
stims[0].setSize(newSize);
|
|
||||||
stims[0].setAnchor("center");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// check for quit (typically the Esc key)
|
|
||||||
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0)
|
|
||||||
{
|
|
||||||
continueRoutine = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the Routine should terminate
|
|
||||||
if (!continueRoutine) { // a component has requested a forced-end of Routine
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
continueRoutine = false; // reverts to True if at least one component still running
|
|
||||||
for (const thisComponent of gaborComponents)
|
|
||||||
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
|
|
||||||
continueRoutine = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh the screen if continuing
|
|
||||||
if (continueRoutine) {
|
|
||||||
return Scheduler.Event.FLIP_REPEAT;
|
|
||||||
} else {
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function gaborRoutineEnd() {
|
|
||||||
return async function () {
|
|
||||||
//------Ending Routine 'gabor'-------
|
|
||||||
for (const thisComponent of gaborComponents) {
|
|
||||||
if (typeof thisComponent.setAutoDraw === 'function') {
|
|
||||||
thisComponent.setAutoDraw(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// the Routine "gabor" was not non-slip safe, so reset the non-slip timer
|
|
||||||
routineTimer.reset();
|
|
||||||
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function endLoopIteration(scheduler, snapshot) {
|
|
||||||
// ------Prepare for next entry------
|
|
||||||
return async function () {
|
|
||||||
if (typeof snapshot !== 'undefined') {
|
|
||||||
// ------Check if user ended loop early------
|
|
||||||
if (snapshot.finished) {
|
|
||||||
// Check for and save orphaned data
|
|
||||||
if (psychoJS.experiment.isEntryEmpty()) {
|
|
||||||
psychoJS.experiment.nextEntry(snapshot);
|
|
||||||
}
|
|
||||||
scheduler.stop();
|
|
||||||
} else {
|
|
||||||
const thisTrial = snapshot.getCurrentTrial();
|
|
||||||
if (typeof thisTrial === 'undefined' || !('isTrials' in thisTrial) || thisTrial.isTrials) {
|
|
||||||
psychoJS.experiment.nextEntry(snapshot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function importConditions(currentLoop) {
|
|
||||||
return async function () {
|
|
||||||
psychoJS.importAttributes(currentLoop.getCurrentTrial());
|
|
||||||
return Scheduler.Event.NEXT;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function quitPsychoJS(message, isCompleted) {
|
|
||||||
// Check for and save orphaned data
|
|
||||||
if (psychoJS.experiment.isEntryEmpty()) {
|
|
||||||
psychoJS.experiment.nextEntry();
|
|
||||||
}
|
|
||||||
psychoJS.window.close();
|
|
||||||
psychoJS.quit({message: message, isCompleted: isCompleted});
|
|
||||||
|
|
||||||
return Scheduler.Event.QUIT;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user