diff --git a/package.json b/package.json index f1476bb..ea75245 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "psychojs", - "version": "2024.1.0", + "version": "2024.2.0", "private": true, "description": "Helps run in-browser neuroscience, psychology, and psychophysics experiments", "license": "MIT", diff --git a/src/test_experiment.js b/src/test_experiment.js deleted file mode 100644 index 1919049..0000000 --- a/src/test_experiment.js +++ /dev/null @@ -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; -}