From b6125d5b1694c85195db8c001548a9c54f72dae0 Mon Sep 17 00:00:00 2001 From: Alain Pitiot Date: Fri, 23 Jul 2021 08:00:20 +0200 Subject: [PATCH] polished up QuestHandler, various comestic improvements, small fixes to Camera --- src/core/PsychoJS.js | 23 +++++++++------- src/core/ServerManager.js | 16 +++++------- src/data/ExperimentHandler.js | 4 +-- src/data/QuestHandler.js | 49 +++++++++++++++++++++++++++++------ src/util/Util.js | 5 ++++ src/visual/Camera.js | 9 ++++--- 6 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/core/PsychoJS.js b/src/core/PsychoJS.js index 4f9d832..cfe2ddc 100644 --- a/src/core/PsychoJS.js +++ b/src/core/PsychoJS.js @@ -145,9 +145,9 @@ export class PsychoJS psychoJS: this }); - // to be loading `configURL` files in `_configure` calls from - const hostsEvidently = new Set([...hosts, 'https://pavlovia.org/run/', 'https://run.pavlovia.org/']); - this._hosts = Array.from(hostsEvidently); + // add the pavlovia server to the list of hosts: + const hostsWithPavlovia = new Set([...hosts, 'https://pavlovia.org/run/', 'https://run.pavlovia.org/']); + this._hosts = Array.from(hostsWithPavlovia); // GUI: this._gui = new GUI(this); @@ -181,7 +181,7 @@ export class PsychoJS this.logger.info('[PsychoJS] Initialised.'); this.logger.info('[PsychoJS] @version 2021.2.x'); - // Hide #root::after + // hide the initialisation message: jQuery('#root').addClass('is-ready'); } @@ -591,17 +591,17 @@ export class PsychoJS { this.status = PsychoJS.Status.CONFIGURING; - // if the experiment is running from the pavlovia.org server, we read the configuration file: + // if the experiment is running from an approved hosts, e.e pavlovia.org, + // we read the configuration file: const experimentUrl = window.location.href; - // go through each url in allow list const isHost = this._hosts.some(url => experimentUrl.indexOf(url) === 0); if (isHost) { const serverResponse = await this._serverManager.getConfiguration(configURL); this._config = serverResponse.config; - // legacy experiments had a psychoJsManager block instead of a pavlovia block, - // and the URL pointed to https://pavlovia.org/server + // update the configuration for legacy experiments, which had a psychoJsManager + // block instead of a pavlovia block, with URL pointing to https://pavlovia.org/server if ('psychoJsManager' in this._config) { delete this._config.psychoJsManager; @@ -744,10 +744,13 @@ export class PsychoJS window.onunhandledrejection = function (error) { console.error(error?.reason); - if (error?.reason?.stack === undefined) { + if (error?.reason?.stack === undefined) + { // No stack? Error thrown by PsychoJS; stringify whole error document.body.setAttribute('data-error', JSON.stringify(error?.reason)); - } else { + } + else + { // Yes stack? Error thrown by JS; stringify stack document.body.setAttribute('data-error', JSON.stringify(error?.reason?.stack)); } diff --git a/src/core/ServerManager.js b/src/core/ServerManager.js index 060e0a0..0dfa238 100644 --- a/src/core/ServerManager.js +++ b/src/core/ServerManager.js @@ -445,8 +445,7 @@ export class ServerManager extends PsychObject // if the experiment is hosted on the pavlovia.org server and // resources is [ServerManager.ALL_RESOURCES], then we register all the resources // in the "resources" sub-directory - if (this._psychoJS.config.environment === ExperimentHandler.Environment.SERVER - && allResources) + if (this._psychoJS.config.environment === ExperimentHandler.Environment.SERVER && allResources) { // list the resources from the resources directory of the experiment on the server: const serverResponse = await this._listResources(); @@ -475,8 +474,7 @@ export class ServerManager extends PsychObject { // we cannot ask for all resources to be registered locally, since we cannot list // them: - if (this._psychoJS.config.environment === ExperimentHandler.Environment.LOCAL - && allResources) + if (this._psychoJS.config.environment === ExperimentHandler.Environment.LOCAL && allResources) { throw "resources must be manually specified when the experiment is running locally: ALL_RESOURCES cannot be used"; } @@ -818,11 +816,11 @@ export class ServerManager extends PsychObject // query the pavlovia server: const response = await fetch(url, { method: 'POST', - mode: 'cors', // no-cors, *cors, same-origin - cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached - credentials: 'same-origin', // include, *same-origin, omit - redirect: 'follow', // manual, *follow, error - referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + mode: 'cors', + cache: 'no-cache', + credentials: 'same-origin', + redirect: 'follow', + referrerPolicy: 'no-referrer', body: formData }); const jsonResponse = await response.json(); diff --git a/src/data/ExperimentHandler.js b/src/data/ExperimentHandler.js index 50a2d60..57558ba 100644 --- a/src/data/ExperimentHandler.js +++ b/src/data/ExperimentHandler.js @@ -33,7 +33,7 @@ export class ExperimentHandler extends PsychObject /** * Getter for experimentEnded. * - * @name module:core.Window#experimentEnded + * @name module:data.ExperimentHandler#experimentEnded * @function * @public */ @@ -45,7 +45,7 @@ export class ExperimentHandler extends PsychObject /** * Setter for experimentEnded. * - * @name module:core.Window#experimentEnded + * @name module:data.ExperimentHandler#experimentEnded * @function * @public */ diff --git a/src/data/QuestHandler.js b/src/data/QuestHandler.js index 072b8c8..7b95d08 100644 --- a/src/data/QuestHandler.js +++ b/src/data/QuestHandler.js @@ -14,14 +14,26 @@ import {TrialHandler} from "./TrialHandler"; /** *

A Trial Handler that implements the Quest algorithm for quick measurement of - psychophysical thresholds.

+ psychophysical thresholds.QuestHandler relies on the [jsQuest]{@link https://github.com/kurokida/jsQUEST} library, a port of Prof Dennis Pelli's QUEST algorithm by [Daiichiro Kuroki]{@link https://github.com/kurokida}.

* - * @class - * @extends PsychObject + * @class module.data.QuestHandler + * @extends TrialHandler * @param {Object} options * @param {module:core.PsychoJS} options.psychoJS - the PsychoJS instance + * @param {string} options.varName - the name of the variable / intensity / contrast / threshold manipulated by QUEST + * @param {number} options.startVal - initial guess for the threshold + * @param {number} options.startValSd - standard deviation of the initial guess + * @param {number} options.minVal - minimum value for the threshold + * @param {number} options.maxVal - maximum value for the threshold + * @param {number} [options.pThreshold=0.82] - threshold criterion expressed as probability of getting a correct response * @param {number} options.nTrials - maximum number of trials + * @param {number} options.stopInterval - minimum [5%, 95%] confidence interval required for the loop to stop * @param {module:data.QuestHandler.Method} options.method - the QUEST method + * @param {number} [options.beta=3.5] - steepness of the QUEST psychometric function + * @param {number} [options.delta=0.01] - fraction of trials with blind responses + * @param {number} [options.gamma=0.5] - fraction of trails that would generate a correct response when the threshold is infinitely small + * @param {number} [options.grain=0.01] - quantization of the internal table + * @param {string} options.name - name of the handler * @param {boolean} [options.autoLog= false] - whether or not to log */ export class QuestHandler extends TrialHandler @@ -80,9 +92,11 @@ export class QuestHandler extends TrialHandler /** * Add a response and update the PDF. * + * @name module:data.QuestHandler#addResponse + * @function * @public - * @param{number} response - the response to the trial, must be either 0 (incorrect, - * non-detected) or 1 (correct, detected). + * @param{number} response - the response to the trial, must be either 0 (incorrect or + * non-detected) or 1 (correct or detected). */ addResponse(response) { @@ -110,7 +124,10 @@ export class QuestHandler extends TrialHandler /** * Simulate a response. * - * @param{number} trueValue + * @name module:data.QuestHandler#simulate + * @function + * @public + * @param{number} trueValue - the true, known value of the threshold / contrast / intensity */ simulate(trueValue) { @@ -128,6 +145,9 @@ export class QuestHandler extends TrialHandler /** * Get the mean of the Quest posterior PDF. * + * @name module:data.QuestHandler#mean + * @function + * @public * @returns {number} the mean */ mean() @@ -139,6 +159,9 @@ export class QuestHandler extends TrialHandler /** * Get the standard deviation of the Quest posterior PDF. * + * @name module:data.QuestHandler#sd + * @function + * @public * @returns {number} the standard deviation */ sd() @@ -150,6 +173,9 @@ export class QuestHandler extends TrialHandler /** * Get the mode of the Quest posterior PDF. * + * @name module:data.QuestHandler#mode + * @function + * @public * @returns {number} the mode */ mode() @@ -162,6 +188,9 @@ export class QuestHandler extends TrialHandler /** * Get the standard deviation of the Quest posterior PDF. * + * @name module:data.QuestHandler#quantile + * @function + * @public * @param{number} quantileOrder the quantile order * @returns {number} the quantile */ @@ -174,6 +203,8 @@ export class QuestHandler extends TrialHandler /** * Get an estimate of the 5%-95% confidence interval (CI). * + * @name module:data.QuestHandler#confInterval + * @function * @public * @param{boolean} [getDifference=false] if true, return the width of the CI instead of the CI */ @@ -198,6 +229,8 @@ export class QuestHandler extends TrialHandler /** * Setup the JS Quest object. * + * @name module:data.QuestHandler#_setupJsQuest + * @function * @protected */ _setupJsQuest() @@ -219,6 +252,8 @@ export class QuestHandler extends TrialHandler * Estimate the next value of the QUEST variable, based on the current value * and on the selected QUEST method. * + * @name module:data.QuestHandler#_estimateQuestValue + * @function * @protected */ _estimateQuestValue() @@ -248,7 +283,6 @@ export class QuestHandler extends TrialHandler this._psychoJS.logger.debug(`estimated value for QUEST variable ${this._varName}: ${this._questValue}`); - // check whether we should finish the trial: if (this.thisN > 0 && (this.nRemaining === 0 || this.confInterval(true) < this._stopInterval)) @@ -269,7 +303,6 @@ export class QuestHandler extends TrialHandler return; } - // update the next undefined trial in the trial list, and the associated snapshot: for (let t = 0; t < this._trialList.length; ++t) { diff --git a/src/util/Util.js b/src/util/Util.js index c1dcf60..1d52caa 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -1454,5 +1454,10 @@ export function extensionFromMimeType(mimeType) return '.wav'; } + if (mimeType.indexOf('video/webm') === 0) + { + return '.webm'; + } + return '.dat'; } diff --git a/src/visual/Camera.js b/src/visual/Camera.js index a78fed5..9c414fe 100644 --- a/src/visual/Camera.js +++ b/src/visual/Camera.js @@ -456,14 +456,15 @@ export class Camera extends PsychObject // create a new stream with ideal dimensions: this._stream = await navigator.mediaDevices.getUserMedia({ - video: { + video: true + /*video: { width: { - ideal: 1920 + ideal: 640 //1920 }, height: { - ideal: 1080 + ideal: 480 //1080 } - } + }*/ }); // check the actual width and height: