Only info relating to the trial execution are returned.
@@ -367,20 +353,20 @@ export class ExperimentHandler extends PsychObject
static _getLoopAttributes(loop)
{
// standard trial attributes:
- const properties = ['thisRepN', 'thisTrialN', 'thisN', 'thisIndex', 'stepSizeCurrent', 'ran', 'order'];
+ const properties = ["thisRepN", "thisTrialN", "thisN", "thisIndex", "stepSizeCurrent", "ran", "order"];
let attributes = {};
const loopName = loop.name;
for (const loopProperty in loop)
{
if (properties.includes(loopProperty))
{
- const key = (loopProperty === 'stepSizeCurrent') ? loopName + '.stepSize' : loopName + '.' + loopProperty;
+ const key = (loopProperty === "stepSizeCurrent") ? loopName + ".stepSize" : loopName + "." + loopProperty;
attributes[key] = loop[loopProperty];
}
}
// specific trial attributes:
- if (typeof loop.getCurrentTrial === 'function')
+ if (typeof loop.getCurrentTrial === "function")
{
const currentTrial = loop.getCurrentTrial();
for (const trialProperty in currentTrial)
@@ -404,7 +390,7 @@ export class ExperimentHandler extends PsychObject
else:
names.append(loopName+'.thisTrial')
vals.append(trial)
-
+
// single StairHandler
elif hasattr(loop, 'intensities'):
names.append(loopName+'.intensity')
@@ -415,10 +401,8 @@ export class ExperimentHandler extends PsychObject
return attributes;
}
-
}
-
/**
* Experiment result format
*
@@ -431,15 +415,14 @@ ExperimentHandler.SaveFormat = {
/**
* Results are saved to a .csv file
*/
- CSV: Symbol.for('CSV'),
+ CSV: Symbol.for("CSV"),
/**
* Results are saved to a database
*/
- DATABASE: Symbol.for('DATABASE')
+ DATABASE: Symbol.for("DATABASE"),
};
-
/**
* Experiment environment.
*
@@ -448,6 +431,6 @@ ExperimentHandler.SaveFormat = {
* @public
*/
ExperimentHandler.Environment = {
- SERVER: Symbol.for('SERVER'),
- LOCAL: Symbol.for('LOCAL')
+ SERVER: Symbol.for("SERVER"),
+ LOCAL: Symbol.for("LOCAL"),
};
diff --git a/src/data/QuestHandler.js b/src/data/QuestHandler.js
index 7b95d08..993d0a6 100644
--- a/src/data/QuestHandler.js
+++ b/src/data/QuestHandler.js
@@ -10,11 +10,11 @@
-import {TrialHandler} from "./TrialHandler";
+import {TrialHandler} from "./TrialHandler.js";
/**
* A Trial Handler that implements the Quest algorithm for quick measurement of
- 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}.
+ 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 module.data.QuestHandler
* @extends TrialHandler
diff --git a/src/data/TrialHandler.js b/src/data/TrialHandler.js
index 47b75a4..098afa7 100644
--- a/src/data/TrialHandler.js
+++ b/src/data/TrialHandler.js
@@ -9,11 +9,10 @@
* @license Distributed under the terms of the MIT License
*/
-
-import seedrandom from 'seedrandom';
-import * as XLSX from 'xlsx';
-import {PsychObject} from '../util/PsychObject';
-import * as util from '../util/Util';
+import seedrandom from "seedrandom";
+import * as XLSX from "xlsx";
+import { PsychObject } from "../util/PsychObject.js";
+import * as util from "../util/Util.js";
/**
* A Trial Handler handles the importing and sequencing of conditions.
@@ -31,7 +30,6 @@ import * as util from '../util/Util';
*/
export class TrialHandler extends PsychObject
{
-
/**
* Getter for experimentHandler.
*
@@ -56,7 +54,6 @@ export class TrialHandler extends PsychObject
this._experimentHandler = exp;
}
-
/**
* @constructor
* @public
@@ -64,27 +61,27 @@ export class TrialHandler extends PsychObject
* @todo extraInfo is not taken into account, we use the expInfo of the ExperimentHandler instead
*/
constructor({
- psychoJS,
- trialList = [undefined],
- nReps,
- method = TrialHandler.Method.RANDOM,
- extraInfo = [],
- seed,
- name,
- autoLog = true
- } = {})
+ psychoJS,
+ trialList = [undefined],
+ nReps,
+ method = TrialHandler.Method.RANDOM,
+ extraInfo = [],
+ seed,
+ name,
+ autoLog = true,
+ } = {})
{
super(psychoJS);
- this._addAttribute('trialList', trialList);
- this._addAttribute('nReps', nReps);
- this._addAttribute('method', method);
- this._addAttribute('extraInfo', extraInfo);
- this._addAttribute('name', name);
- this._addAttribute('autoLog', autoLog);
- this._addAttribute('seed', seed);
+ this._addAttribute("trialList", trialList);
+ this._addAttribute("nReps", nReps);
+ this._addAttribute("method", method);
+ this._addAttribute("extraInfo", extraInfo);
+ this._addAttribute("name", name);
+ this._addAttribute("autoLog", autoLog);
+ this._addAttribute("seed", seed);
this._prepareTrialList(trialList);
-
+
// number of stimuli
this.nStim = this.trialList.length;
@@ -112,7 +109,6 @@ export class TrialHandler extends PsychObject
// array of current snapshots:
this._snapshots = [];
-
// setup the trial sequence:
this._prepareSequence();
@@ -121,18 +117,17 @@ export class TrialHandler extends PsychObject
this._finished = false;
}
-
/**
* Helps go through each trial in the sequence one by one, mirrors PsychoPy.
*/
- next() {
+ next()
+ {
const trialIterator = this[Symbol.iterator]();
const { value } = trialIterator.next();
return value;
}
-
/**
* Iterator over the trial sequence.
*
@@ -168,7 +163,7 @@ export class TrialHandler extends PsychObject
if (this.thisRepN >= this.nReps)
{
this.thisTrial = null;
- return {done: true};
+ return { done: true };
}
this.thisIndex = this._trialSequence[this.thisRepN][this.thisTrialN];
@@ -181,12 +176,11 @@ export class TrialHandler extends PsychObject
vals = (self.thisRepN, self.thisTrialN, self.thisTrial)
logging.exp(msg % vals, obj=self.thisTrial)*/
- return {value: this.thisTrial, done: false};
- }
+ return { value: this.thisTrial, done: false };
+ },
};
}
-
/**
* Execute the callback for each trial in the sequence.
*
@@ -208,7 +202,6 @@ export class TrialHandler extends PsychObject
}
}
-
/**
* @typedef {Object} Snapshot
* @property {TrialHandler} handler - the trialHandler
@@ -253,12 +246,12 @@ export class TrialHandler extends PsychObject
getCurrentTrial: () => this.getTrial(currentIndex),
getTrial: (index = 0) => this.getTrial(index),
- addData: (key, value) => this.addData(key, value)
+ addData: (key, value) => this.addData(key, value),
};
// add to the snapshots the current trial's attributes:
const currentTrial = this.getCurrentTrial();
- const excludedAttributes = ['handler', 'name', 'nStim', 'nRemaining', 'thisRepN', 'thisTrialN', 'thisN', 'thisIndex', 'ran', 'finished'];
+ const excludedAttributes = ["handler", "name", "nStim", "nRemaining", "thisRepN", "thisTrialN", "thisN", "thisIndex", "ran", "finished"];
const trialAttributes = [];
for (const attribute in currentTrial)
{
@@ -280,7 +273,6 @@ export class TrialHandler extends PsychObject
return snapshot;
}
-
/**
* Setter for the seed attribute.
*
@@ -289,9 +281,9 @@ export class TrialHandler extends PsychObject
*/
setSeed(seed, log)
{
- this._setAttribute('seed', seed, log);
+ this._setAttribute("seed", seed, log);
- if (typeof seed !== 'undefined')
+ if (typeof seed !== "undefined")
{
this._randomNumberGenerator = seedrandom(seed);
}
@@ -301,7 +293,6 @@ export class TrialHandler extends PsychObject
}
}
-
/**
* Set the internal state of this trial handler from the given snapshot.
*
@@ -312,12 +303,11 @@ export class TrialHandler extends PsychObject
static fromSnapshot(snapshot)
{
// if snapshot is undefined, do nothing:
- if (typeof snapshot === 'undefined')
+ if (typeof snapshot === "undefined")
{
return;
}
-
snapshot.handler.nStim = snapshot.nStim;
snapshot.handler.nTotal = snapshot.nTotal;
snapshot.handler.nRemaining = snapshot.nRemaining;
@@ -330,13 +320,12 @@ export class TrialHandler extends PsychObject
snapshot.handler.thisTrial = snapshot.handler.getCurrentTrial();
-
// add the snapshot's trial attributes to a global variable, whose name is derived from
// that of the handler: loops -> thisLoop (note the dropped s):
let name = snapshot.name;
- if (name[name.length-1] === 's')
+ if (name[name.length - 1] === "s")
{
- name = name.substr(0, name.length-1);
+ name = name.substr(0, name.length - 1);
}
name = `this${name[0].toUpperCase()}${name.substr(1)}`;
@@ -348,7 +337,6 @@ export class TrialHandler extends PsychObject
window[name] = value;
}
-
/**
* Getter for the finished attribute.
*
@@ -359,7 +347,6 @@ export class TrialHandler extends PsychObject
return this._finished;
}
-
/**
* Setter for the finished attribute.
*
@@ -368,14 +355,13 @@ export class TrialHandler extends PsychObject
set finished(isFinished)
{
this._finished = isFinished;
-
- this._snapshots.forEach( snapshot =>
+
+ this._snapshots.forEach((snapshot) =>
{
snapshot.finished = isFinished;
});
}
-
/**
* Get the trial index.
*
@@ -387,7 +373,6 @@ export class TrialHandler extends PsychObject
return this.thisIndex;
}
-
/**
* Set the trial index.
*
@@ -398,7 +383,6 @@ export class TrialHandler extends PsychObject
this.thisIndex = index;
}
-
/**
* Get the attributes of the trials.
*
@@ -424,7 +408,6 @@ export class TrialHandler extends PsychObject
return Object.keys(this.trialList[0]);
}
-
/**
* Get the current trial.
*
@@ -436,7 +419,6 @@ export class TrialHandler extends PsychObject
return this.trialList[this.thisIndex];
}
-
/**
* Get the nth trial.
*
@@ -453,7 +435,6 @@ export class TrialHandler extends PsychObject
return this.trialList[index];
}
-
/**
* Get the nth future or past trial, without advancing through the trial list.
*
@@ -472,7 +453,6 @@ export class TrialHandler extends PsychObject
return this.trialList[this.thisIndex + n];
}
-
/**
* Get the nth previous trial.
* Note: this is useful for comparisons in n-back tasks.
@@ -486,7 +466,6 @@ export class TrialHandler extends PsychObject
return getFutureTrial(-abs(n));
}
-
/**
* Add a key/value pair to data about the current trial held by the experiment handler
*
@@ -502,7 +481,6 @@ export class TrialHandler extends PsychObject
}
}
-
/**
* Import a list of conditions from a .xls, .xlsx, .odp, or .csv resource.
*
@@ -542,8 +520,8 @@ export class TrialHandler extends PsychObject
{
try
{
- let resourceExtension = resourceName.split('.').pop();
- if (['csv', 'odp', 'xls', 'xlsx'].indexOf(resourceExtension) > -1)
+ let resourceExtension = resourceName.split(".").pop();
+ if (["csv", "odp", "xls", "xlsx"].indexOf(resourceExtension) > -1)
{
// (*) read conditions from resource:
const resourceValue = serverManager.getResource(resourceName, true);
@@ -552,20 +530,20 @@ export class TrialHandler extends PsychObject
// which is then read in as a string
const decodedResourceMaybe = new Uint8Array(resourceValue);
// Could be set to 'buffer' for ASCII .csv
- const type = resourceExtension === 'csv' ? 'string' : 'array';
- const decodedResource = type === 'string' ? (new TextDecoder()).decode(decodedResourceMaybe) : decodedResourceMaybe;
+ const type = resourceExtension === "csv" ? "string" : "array";
+ const decodedResource = type === "string" ? (new TextDecoder()).decode(decodedResourceMaybe) : decodedResourceMaybe;
const workbook = XLSX.read(decodedResource, { type });
// we consider only the first worksheet:
if (workbook.SheetNames.length === 0)
{
- throw 'workbook should contain at least one worksheet';
+ throw "workbook should contain at least one worksheet";
}
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
// worksheet to array of arrays (the first array contains the fields):
- const sheet = XLSX.utils.sheet_to_json(worksheet, {header: 1, blankrows: false});
+ const sheet = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false });
const fields = sheet.shift();
// (*) select conditions:
@@ -574,8 +552,8 @@ export class TrialHandler extends PsychObject
// (*) return the selected conditions as an array of 'object as map':
// [
// {field0: value0-0, field1: value0-1, ...}
- // {field0: value1-0, field1: value1-1, ...}
- // ...
+ // {field0: value1-0, field1: value1-1, ...}
+ // ...
// ]
let trialList = new Array(selectedRows.length - 1);
for (let r = 0; r < selectedRows.length; ++r)
@@ -598,7 +576,7 @@ export class TrialHandler extends PsychObject
value = arrayMaybe;
}
- if (typeof value === 'string')
+ if (typeof value === "string")
{
const numberMaybe = Number.parseFloat(value);
@@ -610,7 +588,7 @@ export class TrialHandler extends PsychObject
else
{
// Parse doubly escaped line feeds
- value = value.replace(/(\n)/g, '\n');
+ value = value.replace(/(\n)/g, "\n");
}
}
@@ -621,23 +599,21 @@ export class TrialHandler extends PsychObject
return trialList;
}
-
else
{
- throw 'extension: ' + resourceExtension + ' currently not supported.';
+ throw "extension: " + resourceExtension + " currently not supported.";
}
}
catch (error)
{
throw {
- origin: 'TrialHandler.importConditions',
+ origin: "TrialHandler.importConditions",
context: `when importing condition: ${resourceName}`,
- error
+ error,
};
}
}
-
/**
* Prepare the trial list.
*
@@ -647,16 +623,15 @@ export class TrialHandler extends PsychObject
_prepareTrialList(trialList)
{
const response = {
- origin: 'TrialHandler._prepareTrialList',
- context: 'when preparing the trial list'
+ origin: "TrialHandler._prepareTrialList",
+ context: "when preparing the trial list",
};
// we treat undefined trialList as a list with a single empty entry:
- if (typeof trialList === 'undefined')
+ if (typeof trialList === "undefined")
{
this.trialList = [undefined];
}
-
// if trialList is an array, we make sure it is not empty:
else if (Array.isArray(trialList))
{
@@ -665,30 +640,27 @@ export class TrialHandler extends PsychObject
this.trialList = [undefined];
}
}
-
// if trialList is a string, we treat it as the name of the condition resource:
- else if (typeof trialList === 'string')
+ else if (typeof trialList === "string")
{
this.trialList = TrialHandler.importConditions(this.psychoJS.serverManager, trialList);
}
-
// unknown type:
else
{
throw Object.assign(response, {
- error: 'unable to prepare trial list: unknown type: ' + (typeof trialList)
+ error: "unable to prepare trial list: unknown type: " + (typeof trialList),
});
}
}
-
/*
* Prepare the sequence of trials.
*
* The returned sequence is a matrix (an array of arrays) of trial indices
* with nStim columns and nReps rows. Note that this is the transpose of the
* matrix return by PsychoPY.
- *
+ *
* Example: with 3 trial and 5 repetitions, we get:
* - sequential:
* [[0 1 2]
@@ -711,8 +683,8 @@ export class TrialHandler extends PsychObject
_prepareSequence()
{
const response = {
- origin: 'TrialHandler._prepareSequence',
- context: 'when preparing a sequence of trials'
+ origin: "TrialHandler._prepareSequence",
+ context: "when preparing a sequence of trials",
};
// get an array of the indices of the elements of trialList :
@@ -722,9 +694,8 @@ export class TrialHandler extends PsychObject
{
this._trialSequence = Array(this.nReps).fill(indices);
// transposed version:
- //this._trialSequence = indices.reduce( (seq, e) => { seq.push( Array(this.nReps).fill(e) ); return seq; }, [] );
+ // this._trialSequence = indices.reduce( (seq, e) => { seq.push( Array(this.nReps).fill(e) ); return seq; }, [] );
}
-
else if (this.method === TrialHandler.Method.RANDOM)
{
this._trialSequence = [];
@@ -733,7 +704,6 @@ export class TrialHandler extends PsychObject
this._trialSequence.push(util.shuffle(indices.slice(), this._randomNumberGenerator));
}
}
-
else if (this.method === TrialHandler.Method.FULL_RANDOM)
{
// create a flat sequence with nReps repeats of indices:
@@ -755,15 +725,13 @@ export class TrialHandler extends PsychObject
}
else
{
- throw Object.assign(response, {error: 'unknown method'});
+ throw Object.assign(response, { error: "unknown method" });
}
return this._trialSequence;
}
-
}
-
/**
* TrialHandler method
*
@@ -775,20 +743,20 @@ TrialHandler.Method = {
/**
* Conditions are presented in the order they are given.
*/
- SEQUENTIAL: Symbol.for('SEQUENTIAL'),
+ SEQUENTIAL: Symbol.for("SEQUENTIAL"),
/**
* Conditions are shuffled within each repeat.
*/
- RANDOM: Symbol.for('RANDOM'),
+ RANDOM: Symbol.for("RANDOM"),
/**
* Conditions are fully randomised across all repeats.
*/
- FULL_RANDOM: Symbol.for('FULL_RANDOM'),
+ FULL_RANDOM: Symbol.for("FULL_RANDOM"),
/**
* Same as above, but named to reflect PsychoPy boileplate.
*/
- FULLRANDOM: Symbol.for('FULL_RANDOM')
+ FULLRANDOM: Symbol.for("FULL_RANDOM"),
};
diff --git a/src/index.css b/src/index.css
index 70f84cd..ced7904 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,7 +1,16 @@
+body {
+ align-items: center;
+ display: flex;
+ height: 100vh;
+ justify-content: center;
+ margin: 0;
+}
+
/* Project and resource dialogs */
label,
input,
select {
+ box-sizing: border-box;
display: block;
padding-bottom: 0.5em;
}
@@ -10,7 +19,7 @@ input.text,
select.text {
margin-bottom: 1em;
padding: 0.5em;
- width: 95%;
+ width: 100%;
}
fieldset {
@@ -32,83 +41,43 @@ a:hover {
}
.progress {
+ box-sizing: border-box;
padding: 0.5em 0;
}
.logo {
display: block;
margin: 0 auto 1em;
- max-width: 100%;
max-height: 20vh;
+ max-width: 100%;
+}
+
+.ui-dialog {
+ margin: auto;
+ max-width: 88vw;
+ position: relative;
}
/* Don't display close button in the top right corner of the box */
-.no-close .ui-dialog-titlebar-close {
+.ui-dialog.no-close .ui-dialog-titlebar-close {
display: none;
}
-.ui-dialog-content {
+.ui-dialog .ui-dialog-content {
margin-top: 1em;
+ max-height: calc(100vh - 12em) !important;
+ overflow-y: auto;
+}
+
+.ui-dialog .ui-dialog-buttonpane {
+ /* Avoid padding related overflow */
+ box-sizing: border-box;
}
@media only screen and (max-width: 1080px) {
- .ui-widget {
- transform: scale(2);
- }
-
- .ui-widget .ui-progressbar {
- transform: scale(1);
- }
-
- .ui-dialog-titlebar .ui-button {
- margin-right: 1em;
- }
-
- .ui-dialog-titlebar .ui-dialog-titlebar-close {
- transform: scale(1);
- }
-
.ui-dialog .ui-dialog-buttonpane {
padding-top: 1em;
}
-
- .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
- transform: scale(1);
- }
-
- .ui-dialog .ui-dialog-titlebar {
- padding: 1em 2em;
- }
-}
-
-@media only screen and (max-width: 1080px) and (orientation: landscape) {
- .ui-widget {
- transform: scale(1.5);
- }
-
- .ui-widget .ui-progressbar {
- transform: scale(1);
- }
-
- .ui-dialog-titlebar .ui-button {
- margin-right: 1em;
- }
-
- .ui-dialog-titlebar .ui-dialog-titlebar-close {
- transform: scale(1);
- }
-
- .ui-dialog .ui-dialog-buttonpane {
- padding-top: 1em;
- }
-
- .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
- transform: scale(1);
- }
-
- .ui-dialog .ui-dialog-titlebar {
- padding: 1em 2em;
- }
}
/* Initialisation message (which will disappear behind the canvas) */
@@ -123,8 +92,8 @@ a:hover {
/* Initialisation message for IE11 */
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
#root::after {
- font-weight: bold;
color: #a05000;
content: "initialising the experiment... | Internet Explorer / Edge [beta]";
+ font-weight: bold;
}
}
diff --git a/src/sound/AudioClip.js b/src/sound/AudioClip.js
index 1c358eb..b01cd1d 100644
--- a/src/sound/AudioClip.js
+++ b/src/sound/AudioClip.js
@@ -7,11 +7,10 @@
* @license Distributed under the terms of the MIT License
*/
-import {PsychObject} from '../util/PsychObject';
-import {PsychoJS} from '../core/PsychoJS';
-import {ExperimentHandler} from '../data/ExperimentHandler';
-import * as util from '../util/Util';
-
+import { PsychoJS } from "../core/PsychoJS.js";
+import { ExperimentHandler } from "../data/ExperimentHandler.js";
+import { PsychObject } from "../util/PsychObject.js";
+import * as util from "../util/Util.js";
/**
*
AudioClip encapsulates an audio recording.
@@ -28,20 +27,19 @@ import * as util from '../util/Util';
*/
export class AudioClip extends PsychObject
{
-
- constructor({psychoJS, name, sampleRateHz, format, data, autoLog} = {})
+ constructor({ psychoJS, name, sampleRateHz, format, data, autoLog } = {})
{
super(psychoJS);
- this._addAttribute('name', name, 'audioclip');
- this._addAttribute('format', format);
- this._addAttribute('sampleRateHz', sampleRateHz);
- this._addAttribute('data', data);
- this._addAttribute('autoLog', false, autoLog);
- this._addAttribute('status', AudioClip.Status.CREATED);
+ this._addAttribute("name", name, "audioclip");
+ this._addAttribute("format", format);
+ this._addAttribute("sampleRateHz", sampleRateHz);
+ this._addAttribute("data", data);
+ this._addAttribute("autoLog", false, autoLog);
+ this._addAttribute("status", AudioClip.Status.CREATED);
// add a volume attribute, for playback:
- this._addAttribute('volume', 1.0);
+ this._addAttribute("volume", 1.0);
if (this._autoLog)
{
@@ -52,7 +50,6 @@ export class AudioClip extends PsychObject
this._decodeAudio();
}
-
/**
* Set the volume of the playback.
*
@@ -66,7 +63,6 @@ export class AudioClip extends PsychObject
this._volume = volume;
}
-
/**
* Start playing the audio clip.
*
@@ -76,7 +72,7 @@ export class AudioClip extends PsychObject
*/
async startPlayback()
{
- this._psychoJS.logger.debug('request to play the audio clip');
+ this._psychoJS.logger.debug("request to play the audio clip");
// wait for the decoding to complete:
await this._decodeAudio();
@@ -103,7 +99,6 @@ export class AudioClip extends PsychObject
this._source.start();
}
-
/**
* Stop playing the audio clip.
*
@@ -120,7 +115,6 @@ export class AudioClip extends PsychObject
this._source.stop();
}
-
/**
* Get the duration of the audio clip, in seconds.
*
@@ -137,7 +131,6 @@ export class AudioClip extends PsychObject
return this._audioBuffer.duration;
}
-
/**
* Upload the audio clip to the pavlovia server.
*
@@ -147,27 +140,26 @@ export class AudioClip extends PsychObject
*/
upload()
{
- this._psychoJS.logger.debug('request to upload the audio clip to pavlovia.org');
+ this._psychoJS.logger.debug("request to upload the audio clip to pavlovia.org");
// add a format-dependent audio extension to the name:
const filename = this._name + util.extensionFromMimeType(this._format);
-
// if the audio recording cannot be uploaded, e.g. the experiment is running locally, or
// if it is piloting mode, then we offer the audio clip as a file for download:
- if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER ||
- this._psychoJS.config.experiment.status !== 'RUNNING' ||
- this._psychoJS._serverMsg.has('__pilotToken'))
+ if (
+ this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
+ || this._psychoJS.config.experiment.status !== "RUNNING"
+ || this._psychoJS._serverMsg.has("__pilotToken")
+ )
{
return this.download(filename);
}
// upload the data:
- return this._psychoJS.serverManager.uploadAudio(this._data, filename);
+ return this._psychoJS.serverManager.uploadAudioVideo(this._data, filename);
}
-
-
/**
* Offer the audio clip to the participant as a sound file to download.
*
@@ -175,9 +167,9 @@ export class AudioClip extends PsychObject
* @function
* @public
*/
- download(filename = 'audio.webm')
+ download(filename = "audio.webm")
{
- const anchor = document.createElement('a');
+ const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(this._data);
anchor.download = filename;
document.body.appendChild(anchor);
@@ -185,7 +177,6 @@ export class AudioClip extends PsychObject
document.body.removeChild(anchor);
}
-
/**
* Transcribe the audio clip.
*
@@ -196,10 +187,10 @@ export class AudioClip extends PsychObject
* @return {Promise<>} a promise resolving to the transcript and associated
* transcription confidence
*/
- async transcribe({engine, languageCode} = {})
+ async transcribe({ engine, languageCode } = {})
{
const response = {
- origin: 'AudioClip.transcribe',
+ origin: "AudioClip.transcribe",
context: `when transcribing audio clip: ${this._name}`,
};
@@ -215,11 +206,11 @@ export class AudioClip extends PsychObject
transcriptionKey = key.value;
}
}
- if (typeof transcriptionKey === 'undefined')
+ if (typeof transcriptionKey === "undefined")
{
throw {
...response,
- error: `missing key for engine: ${fullEngineName}`
+ error: `missing key for engine: ${fullEngineName}`,
};
}
@@ -235,13 +226,11 @@ export class AudioClip extends PsychObject
{
throw {
...response,
- error: `unsupported speech-to-text engine: ${engine}`
+ error: `unsupported speech-to-text engine: ${engine}`,
};
}
-
}
-
/**
* Transcribe the audio clip using the Google Cloud Speech-To-Text Engine.
*
@@ -272,31 +261,31 @@ export class AudioClip extends PsychObject
// query the Google speech-to-text service:
const body = {
config: {
- encoding: 'LINEAR16',
+ encoding: "LINEAR16",
sampleRateHertz: this._sampleRateHz,
- languageCode
+ languageCode,
},
audio: {
- content: base64Data
+ content: base64Data,
},
};
const url = `https://speech.googleapis.com/v1/speech:recognize?key=${transcriptionKey}`;
const response = await fetch(url, {
- method: 'POST',
+ method: "POST",
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
- body: JSON.stringify(body)
+ body: JSON.stringify(body),
});
// convert the response to json:
const decodedResponse = await response.json();
- this._psychoJS.logger.debug('speech.googleapis.com response:', JSON.stringify(decodedResponse));
+ this._psychoJS.logger.debug("speech.googleapis.com response:", JSON.stringify(decodedResponse));
// TODO deal with more than one results and/or alternatives
- if (('results' in decodedResponse) && (decodedResponse.results.length > 0))
+ if (("results" in decodedResponse) && (decodedResponse.results.length > 0))
{
resolve(decodedResponse.results[0].alternatives[0]);
}
@@ -304,21 +293,20 @@ export class AudioClip extends PsychObject
{
// no transcription available:
resolve({
- transcript: '',
- confidence: -1
+ transcript: "",
+ confidence: -1,
});
}
});
}
-
/**
* Decode the formatted audio data (e.g. webm) into a 32bit float PCM audio buffer.
*
*/
_decodeAudio()
{
- this._psychoJS.logger.debug('request to decode the data of the audio clip');
+ this._psychoJS.logger.debug("request to decode the data of the audio clip");
// if the audio clip is ready, the PCM audio data is available in _audioData, a Float32Array:
if (this._status === AudioClip.Status.READY)
@@ -326,12 +314,11 @@ export class AudioClip extends PsychObject
return;
}
-
// if we are already decoding, wait until the process completed:
if (this._status === AudioClip.Status.DECODING)
{
const self = this;
- return new Promise(function (resolve, reject)
+ return new Promise(function(resolve, reject)
{
self._decodingCallbacks.push(resolve);
@@ -339,7 +326,6 @@ export class AudioClip extends PsychObject
}.bind(this));
}
-
// otherwise, start decoding the input formatted audio data:
this._status = AudioClip.Status.DECODING;
this._audioData = null;
@@ -348,7 +334,7 @@ export class AudioClip extends PsychObject
this._decodingCallbacks = [];
this._audioContext = new (window.AudioContext || window.webkitAudioContext)({
- sampleRate: this._sampleRateHz
+ sampleRate: this._sampleRateHz,
});
const reader = new window.FileReader();
@@ -383,12 +369,11 @@ export class AudioClip extends PsychObject
reader.onerror = (error) =>
{
// TODO
- }
+ };
reader.readAsArrayBuffer(this._data);
}
-
/**
* Convert an array buffer to a base64 string.
*
@@ -403,63 +388,65 @@ export class AudioClip extends PsychObject
*/
_base64ArrayBuffer(arrayBuffer)
{
- let base64 = '';
- const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ let base64 = "";
+ const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- const bytes = new Uint8Array(arrayBuffer);
- const byteLength = bytes.byteLength;
- const byteRemainder = byteLength % 3;
- const mainLength = byteLength - byteRemainder;
+ const bytes = new Uint8Array(arrayBuffer);
+ const byteLength = bytes.byteLength;
+ const byteRemainder = byteLength % 3;
+ const mainLength = byteLength - byteRemainder;
- let a;
- let b;
- let c;
- let d;
- let chunk;
+ let a;
+ let b;
+ let c;
+ let d;
+ let chunk;
- // Main loop deals with bytes in chunks of 3
- for (let i = 0; i < mainLength; i += 3) {
- // Combine the three bytes into a single integer
- chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
+ // Main loop deals with bytes in chunks of 3
+ for (let i = 0; i < mainLength; i += 3)
+ {
+ // Combine the three bytes into a single integer
+ chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
- // Use bitmasks to extract 6-bit segments from the triplet
- a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
- b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
- c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
- d = chunk & 63; // 63 = 2^6 - 1
+ // Use bitmasks to extract 6-bit segments from the triplet
+ a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
+ b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
+ c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
+ d = chunk & 63; // 63 = 2^6 - 1
- // Convert the raw binary segments to the appropriate ASCII encoding
- base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
+ // Convert the raw binary segments to the appropriate ASCII encoding
+ base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
+ }
+
+ // Deal with the remaining bytes and padding
+ if (byteRemainder === 1)
+ {
+ chunk = bytes[mainLength];
+
+ a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
+
+ // Set the 4 least significant bits to zero
+ b = (chunk & 3) << 4; // 3 = 2^2 - 1
+
+ base64 += `${encodings[a]}${encodings[b]}==`;
+ }
+ else if (byteRemainder === 2)
+ {
+ chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
+
+ a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
+ b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
+
+ // Set the 2 least significant bits to zero
+ c = (chunk & 15) << 2; // 15 = 2^4 - 1
+
+ base64 += `${encodings[a]}${encodings[b]}${encodings[c]}=`;
+ }
+
+ return base64;
}
-
- // Deal with the remaining bytes and padding
- if (byteRemainder === 1) {
- chunk = bytes[mainLength];
-
- a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
-
- // Set the 4 least significant bits to zero
- b = (chunk & 3) << 4; // 3 = 2^2 - 1
-
- base64 += `${encodings[a]}${encodings[b]}==`;
- } else if (byteRemainder === 2) {
- chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
-
- a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
- b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
-
- // Set the 2 least significant bits to zero
- c = (chunk & 15) << 2; // 15 = 2^4 - 1
-
- base64 += `${encodings[a]}${encodings[b]}${encodings[c]}=`;
- }
-
- return base64;
}
-}
-
-
/**
* Recognition engines.
*
@@ -472,10 +459,9 @@ AudioClip.Engine = {
/**
* Google Cloud Speech-to-Text.
*/
- GOOGLE: Symbol.for('GOOGLE')
+ GOOGLE: Symbol.for("GOOGLE"),
};
-
/**
* AudioClip status.
*
@@ -484,9 +470,9 @@ AudioClip.Engine = {
* @public
*/
AudioClip.Status = {
- CREATED: Symbol.for('CREATED'),
+ CREATED: Symbol.for("CREATED"),
- DECODING: Symbol.for('DECODING'),
+ DECODING: Symbol.for("DECODING"),
- READY: Symbol.for('READY')
+ READY: Symbol.for("READY"),
};
diff --git a/src/sound/AudioClipPlayer.js b/src/sound/AudioClipPlayer.js
index eb53947..082a71a 100644
--- a/src/sound/AudioClipPlayer.js
+++ b/src/sound/AudioClipPlayer.js
@@ -7,9 +7,8 @@
* @license Distributed under the terms of the MIT License
*/
-import {SoundPlayer} from './SoundPlayer';
-import {AudioClip} from "./AudioClip";
-
+import { AudioClip } from "./AudioClip.js";
+import { SoundPlayer } from "./SoundPlayer.js";
/**
* This class handles the playback of an audio clip, e.g. a microphone recording.
@@ -29,28 +28,27 @@ import {AudioClip} from "./AudioClip";
export class AudioClipPlayer extends SoundPlayer
{
constructor({
- psychoJS,
- audioClip,
- startTime = 0,
- stopTime = -1,
- stereo = true,
- volume = 0,
- loops = 0
- } = {})
+ psychoJS,
+ audioClip,
+ startTime = 0,
+ stopTime = -1,
+ stereo = true,
+ volume = 0,
+ loops = 0,
+ } = {})
{
super(psychoJS);
- this._addAttribute('audioClip', audioClip);
- this._addAttribute('startTime', startTime);
- this._addAttribute('stopTime', stopTime);
- this._addAttribute('stereo', stereo);
- this._addAttribute('loops', loops);
- this._addAttribute('volume', volume);
+ this._addAttribute("audioClip", audioClip);
+ this._addAttribute("startTime", startTime);
+ this._addAttribute("stopTime", stopTime);
+ this._addAttribute("stereo", stereo);
+ this._addAttribute("loops", loops);
+ this._addAttribute("volume", volume);
this._currentLoopIndex = -1;
}
-
/**
* Determine whether this player can play the given sound.
*
@@ -73,7 +71,7 @@ export class AudioClipPlayer extends SoundPlayer
stopTime: sound.stopTime,
stereo: sound.stereo,
loops: sound.loops,
- volume: sound.volume
+ volume: sound.volume,
});
return player;
}
@@ -82,7 +80,6 @@ export class AudioClipPlayer extends SoundPlayer
return undefined;
}
-
/**
* Get the duration of the AudioClip, in seconds.
*
@@ -96,7 +93,6 @@ export class AudioClipPlayer extends SoundPlayer
return this._audioClip.getDuration();
}
-
/**
* Set the duration of the audio clip.
*
@@ -110,13 +106,12 @@ export class AudioClipPlayer extends SoundPlayer
// TODO
throw {
- origin: 'AudioClipPlayer.setDuration',
- context: 'when setting the duration of the playback for audio clip player: ' + this._name,
- error: 'not implemented yet'
+ origin: "AudioClipPlayer.setDuration",
+ context: "when setting the duration of the playback for audio clip player: " + this._name,
+ error: "not implemented yet",
};
}
-
/**
* Set the volume of the playback.
*
@@ -133,7 +128,6 @@ export class AudioClipPlayer extends SoundPlayer
this._audioClip.setVolume((mute) ? 0.0 : volume);
}
-
/**
* Set the number of loops.
*
@@ -150,7 +144,6 @@ export class AudioClipPlayer extends SoundPlayer
// TODO
}
-
/**
* Start playing the sound.
*
@@ -162,7 +155,7 @@ export class AudioClipPlayer extends SoundPlayer
*/
play(loops, fadeDuration = 17)
{
- if (typeof loops !== 'undefined')
+ if (typeof loops !== "undefined")
{
this.setLoops(loops);
}
@@ -176,7 +169,6 @@ export class AudioClipPlayer extends SoundPlayer
this._audioClip.startPlayback();
}
-
/**
* Stop playing the sound immediately.
*
@@ -189,5 +181,4 @@ export class AudioClipPlayer extends SoundPlayer
{
this._audioClip.stopPlayback(fadeDuration);
}
-
}
diff --git a/src/sound/Microphone.js b/src/sound/Microphone.js
index b0d0e21..b346ac6 100644
--- a/src/sound/Microphone.js
+++ b/src/sound/Microphone.js
@@ -7,12 +7,12 @@
* @license Distributed under the terms of the MIT License
*/
-import {Clock} from "../util/Clock";
-import {PsychObject} from "../util/PsychObject";
-import {PsychoJS} from "../core/PsychoJS";
-import * as util from '../util/Util';
-import {ExperimentHandler} from "../data/ExperimentHandler";
-import {AudioClip} from "./AudioClip";
+import { PsychoJS } from "../core/PsychoJS.js";
+import { ExperimentHandler } from "../data/ExperimentHandler.js";
+import { Clock } from "../util/Clock.js";
+import { PsychObject } from "../util/PsychObject.js";
+import * as util from "../util/Util.js";
+import { AudioClip } from "./AudioClip.js";
/**
* This manager handles the recording of audio signal.
@@ -29,18 +29,17 @@ import {AudioClip} from "./AudioClip";
*/
export class Microphone extends PsychObject
{
-
- constructor({win, name, format, sampleRateHz, clock, autoLog} = {})
+ constructor({ win, name, format, sampleRateHz, clock, autoLog } = {})
{
super(win._psychoJS);
- this._addAttribute('win', win, undefined);
- this._addAttribute('name', name, 'microphone');
- this._addAttribute('format', format, 'audio/webm;codecs=opus', this._onChange);
- this._addAttribute('sampleRateHz', sampleRateHz, 48000, this._onChange);
- this._addAttribute('clock', clock, new Clock());
- this._addAttribute('autoLog', autoLog, false);
- this._addAttribute('status', PsychoJS.Status.NOT_STARTED);
+ this._addAttribute("win", win, undefined);
+ this._addAttribute("name", name, "microphone");
+ this._addAttribute("format", format, "audio/webm;codecs=opus", this._onChange);
+ this._addAttribute("sampleRateHz", sampleRateHz, 48000, this._onChange);
+ this._addAttribute("clock", clock, new Clock());
+ this._addAttribute("autoLog", autoLog, autoLog);
+ this._addAttribute("status", PsychoJS.Status.NOT_STARTED);
// prepare the recording:
this._prepareRecording();
@@ -51,7 +50,6 @@ export class Microphone extends PsychObject
}
}
-
/**
* Submit a request to start the recording.
*
@@ -68,19 +66,18 @@ export class Microphone extends PsychObject
// with a new recording:
if (this._status === PsychoJS.Status.PAUSED)
{
- return this.resume({clear: true});
+ return this.resume({ clear: true });
}
-
if (this._status !== PsychoJS.Status.STARTED)
{
- this._psychoJS.logger.debug('request to start audio recording');
+ this._psychoJS.logger.debug("request to start audio recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
this._recorder.start();
@@ -96,21 +93,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
- this._psychoJS.logger.error('unable to start the audio recording: ' + JSON.stringify(error));
+ this._psychoJS.logger.error("unable to start the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Microphone.start',
- context: 'when starting the audio recording for microphone: ' + this._name,
- error
+ origin: "Microphone.start",
+ context: "when starting the audio recording for microphone: " + this._name,
+ error,
};
}
-
}
-
}
-
/**
* Submit a request to stop the recording.
*
@@ -122,14 +116,14 @@ export class Microphone extends PsychObject
* @return {Promise} promise fulfilled when the recording actually stopped, and the recorded
* data was made available
*/
- stop({filename} = {})
+ stop({ filename } = {})
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to stop audio recording');
+ this._psychoJS.logger.debug("request to stop audio recording");
this._stopOptions = {
- filename
+ filename,
};
// note: calling the stop method of the MediaRecorder will first raise a dataavailable event,
@@ -148,7 +142,6 @@ export class Microphone extends PsychObject
}
}
-
/**
* Submit a request to pause the recording.
*
@@ -160,13 +153,13 @@ export class Microphone extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED)
{
- this._psychoJS.logger.debug('request to pause audio recording');
+ this._psychoJS.logger.debug("request to pause audio recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
// note: calling the pause method of the MediaRecorder raises a pause event
@@ -182,20 +175,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
- self._psychoJS.logger.error('unable to pause the audio recording: ' + JSON.stringify(error));
+ self._psychoJS.logger.error("unable to pause the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Microphone.pause',
- context: 'when pausing the audio recording for microphone: ' + this._name,
- error
+ origin: "Microphone.pause",
+ context: "when pausing the audio recording for microphone: " + this._name,
+ error,
};
}
-
}
}
-
/**
* Submit a request to resume the recording.
*
@@ -207,17 +198,17 @@ export class Microphone extends PsychObject
* resuming the recording
* @return {Promise} promise fulfilled when the recording actually resumed
*/
- resume({clear = false } = {})
+ resume({ clear = false } = {})
{
if (this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to resume audio recording');
+ this._psychoJS.logger.debug("request to resume audio recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
// empty the audio buffer is needed:
@@ -239,20 +230,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
- self._psychoJS.logger.error('unable to resume the audio recording: ' + JSON.stringify(error));
+ self._psychoJS.logger.error("unable to resume the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Microphone.resume',
- context: 'when resuming the audio recording for microphone: ' + this._name,
- error
+ origin: "Microphone.resume",
+ context: "when resuming the audio recording for microphone: " + this._name,
+ error,
};
}
-
}
}
-
/**
* Submit a request to flush the recording.
*
@@ -264,7 +253,7 @@ export class Microphone extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to flush audio recording');
+ this._psychoJS.logger.debug("request to flush audio recording");
// note: calling the requestData method of the MediaRecorder will raise a
// dataavailable event
@@ -281,7 +270,6 @@ export class Microphone extends PsychObject
}
}
-
/**
* Offer the audio recording to the participant as a sound file to download.
*
@@ -290,11 +278,11 @@ export class Microphone extends PsychObject
* @public
* @param {string} filename the filename
*/
- download(filename = 'audio.webm')
+ download(filename = "audio.webm")
{
const audioBlob = new Blob(this._audioBuffer);
- const anchor = document.createElement('a');
+ const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(audioBlob);
anchor.download = filename;
document.body.appendChild(anchor);
@@ -302,7 +290,6 @@ export class Microphone extends PsychObject
document.body.removeChild(anchor);
}
-
/**
* Upload the audio recording to the pavlovia server.
*
@@ -311,10 +298,10 @@ export class Microphone extends PsychObject
* @public
* @param {string} tag an optional tag for the audio file
*/
- async upload({tag} = {})
+ async upload({ tag } = {})
{
// default tag: the name of this Microphone object
- if (typeof tag === 'undefined')
+ if (typeof tag === "undefined")
{
tag = this._name;
}
@@ -322,22 +309,22 @@ export class Microphone extends PsychObject
// add a format-dependent audio extension to the tag:
tag += util.extensionFromMimeType(this._format);
-
// if the audio recording cannot be uploaded, e.g. the experiment is running locally, or
// if it is piloting mode, then we offer the audio recording as a file for download:
- if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER ||
- this._psychoJS.config.experiment.status !== 'RUNNING' ||
- this._psychoJS._serverMsg.has('__pilotToken'))
+ if (
+ this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
+ || this._psychoJS.config.experiment.status !== "RUNNING"
+ || this._psychoJS._serverMsg.has("__pilotToken")
+ )
{
return this.download(tag);
}
// upload the blob:
const audioBlob = new Blob(this._audioBuffer);
- return this._psychoJS.serverManager.uploadAudio(audioBlob, tag);
+ return this._psychoJS.serverManager.uploadAudioVideo(audioBlob, tag);
}
-
/**
* Get the current audio recording as an AudioClip in the given format.
*
@@ -347,27 +334,25 @@ export class Microphone extends PsychObject
* @param {string} tag an optional tag for the audio clip
* @param {boolean} [flush=false] whether or not to first flush the recording
*/
- async getRecording({tag, flush = false} = {})
+ async getRecording({ tag, flush = false } = {})
{
// default tag: the name of this Microphone object
- if (typeof tag === 'undefined')
+ if (typeof tag === "undefined")
{
tag = this._name;
}
-
const audioClip = new AudioClip({
psychoJS: this._psychoJS,
name: tag,
format: this._format,
sampleRateHz: this._sampleRateHz,
- data: new Blob(this._audioBuffer)
+ data: new Blob(this._audioBuffer),
});
return audioClip;
}
-
/**
* Callback for changes to the recording settings.
*
@@ -389,7 +374,6 @@ export class Microphone extends PsychObject
this.start();
}
-
/**
* Prepare the recording.
*
@@ -409,15 +393,15 @@ export class Microphone extends PsychObject
advanced: [
{
channelCount: 1,
- sampleRate: this._sampleRateHz
- }
- ]
- }
+ sampleRate: this._sampleRateHz,
+ },
+ ],
+ },
});
// check that the specified format is supported, use default if it is not:
let options;
- if (typeof this._format === 'string' && MediaRecorder.isTypeSupported(this._format))
+ if (typeof this._format === "string" && MediaRecorder.isTypeSupported(this._format))
{
options = { type: this._format };
}
@@ -428,7 +412,6 @@ export class Microphone extends PsychObject
this._recorder = new MediaRecorder(stream, options);
-
// setup the callbacks:
const self = this;
@@ -440,7 +423,7 @@ export class Microphone extends PsychObject
self._audioBuffer.length = 0;
self._clock.reset();
self._status = PsychoJS.Status.STARTED;
- self._psychoJS.logger.debug('audio recording started');
+ self._psychoJS.logger.debug("audio recording started");
// resolve the Microphone.start promise:
if (self._startCallback)
@@ -453,7 +436,7 @@ export class Microphone extends PsychObject
this._recorder.onpause = () =>
{
self._status = PsychoJS.Status.PAUSED;
- self._psychoJS.logger.debug('audio recording paused');
+ self._psychoJS.logger.debug("audio recording paused");
// resolve the Microphone.pause promise:
if (self._pauseCallback)
@@ -466,7 +449,7 @@ export class Microphone extends PsychObject
this._recorder.onresume = () =>
{
self._status = PsychoJS.Status.STARTED;
- self._psychoJS.logger.debug('audio recording resumed');
+ self._psychoJS.logger.debug("audio recording resumed");
// resolve the Microphone.resume promise:
if (self._resumeCallback)
@@ -482,7 +465,7 @@ export class Microphone extends PsychObject
// add data to the buffer:
self._audioBuffer.push(data);
- self._psychoJS.logger.debug('audio data added to the buffer');
+ self._psychoJS.logger.debug("audio data added to the buffer");
// resolve the data available promise, if needed:
if (self._dataAvailableCallback)
@@ -494,7 +477,7 @@ export class Microphone extends PsychObject
// called upon Microphone.stop(), after data has been made available:
this._recorder.onstop = () =>
{
- self._psychoJS.logger.debug('audio recording stopped');
+ self._psychoJS.logger.debug("audio recording stopped");
self._status = PsychoJS.Status.NOT_STARTED;
// resolve the Microphone.stop promise:
@@ -506,7 +489,7 @@ export class Microphone extends PsychObject
// treat stop options if there are any:
// download to a file, immediately offered to the participant:
- if (typeof self._stopOptions.filename === 'string')
+ if (typeof self._stopOptions.filename === "string")
{
self.download(self._stopOptions.filename);
}
@@ -516,12 +499,8 @@ export class Microphone extends PsychObject
this._recorder.onerror = (event) =>
{
// TODO
- self._psychoJS.logger.error('audio recording error: ' + JSON.stringify(event));
+ self._psychoJS.logger.error("audio recording error: " + JSON.stringify(event));
self._status = PsychoJS.Status.ERROR;
};
-
}
-
}
-
-
diff --git a/src/sound/Sound.js b/src/sound/Sound.js
index 068cf6c..51f1b01 100644
--- a/src/sound/Sound.js
+++ b/src/sound/Sound.js
@@ -8,12 +8,11 @@
* @license Distributed under the terms of the MIT License
*/
-import {PsychoJS} from '../core/PsychoJS';
-import {PsychObject} from '../util/PsychObject';
-import {TonePlayer} from './TonePlayer';
-import {TrackPlayer} from './TrackPlayer';
-import {AudioClipPlayer} from './AudioClipPlayer';
-
+import { PsychoJS } from "../core/PsychoJS.js";
+import { PsychObject } from "../util/PsychObject.js";
+import { AudioClipPlayer } from "./AudioClipPlayer.js";
+import { TonePlayer } from "./TonePlayer.js";
+import { TrackPlayer } from "./TrackPlayer.js";
/**
* SoundPlayer is an interface for the sound players, who are responsible for actually playing the sounds, i.e. the tracks or the tones.
@@ -25,7 +24,6 @@ export class SoundPlayer extends PsychObject
super(psychoJS);
}
-
/**
* Determine whether this player can play the given sound.
*
@@ -40,13 +38,12 @@ export class SoundPlayer extends PsychObject
static accept(sound)
{
throw {
- origin: 'SoundPlayer.accept',
- context: 'when evaluating whether this player can play a given sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.accept",
+ context: "when evaluating whether this player can play a given sound",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Start playing the sound.
*
@@ -59,13 +56,12 @@ export class SoundPlayer extends PsychObject
play(loops)
{
throw {
- origin: 'SoundPlayer.play',
- context: 'when starting the playback of a sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.play",
+ context: "when starting the playback of a sound",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Stop playing the sound immediately.
*
@@ -77,13 +73,12 @@ export class SoundPlayer extends PsychObject
stop()
{
throw {
- origin: 'SoundPlayer.stop',
- context: 'when stopping the playback of a sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.stop",
+ context: "when stopping the playback of a sound",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Get the duration of the sound, in seconds.
*
@@ -95,13 +90,12 @@ export class SoundPlayer extends PsychObject
getDuration()
{
throw {
- origin: 'SoundPlayer.getDuration',
- context: 'when getting the duration of the sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.getDuration",
+ context: "when getting the duration of the sound",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Set the duration of the sound, in seconds.
*
@@ -113,13 +107,12 @@ export class SoundPlayer extends PsychObject
setDuration(duration_s)
{
throw {
- origin: 'SoundPlayer.setDuration',
- context: 'when setting the duration of the sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.setDuration",
+ context: "when setting the duration of the sound",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Set the number of loops.
*
@@ -132,13 +125,12 @@ export class SoundPlayer extends PsychObject
setLoops(loops)
{
throw {
- origin: 'SoundPlayer.setLoops',
- context: 'when setting the number of loops',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.setLoops",
+ context: "when setting the number of loops",
+ error: "this method is abstract and should not be called.",
};
}
-
/**
* Set the volume of the tone.
*
@@ -152,10 +144,9 @@ export class SoundPlayer extends PsychObject
setVolume(volume, mute = false)
{
throw {
- origin: 'SoundPlayer.setVolume',
- context: 'when setting the volume of the sound',
- error: 'this method is abstract and should not be called.'
+ origin: "SoundPlayer.setVolume",
+ context: "when setting the volume of the sound",
+ error: "this method is abstract and should not be called.",
};
}
-
}
diff --git a/src/sound/TonePlayer.js b/src/sound/TonePlayer.js
index dbac89f..a552162 100644
--- a/src/sound/TonePlayer.js
+++ b/src/sound/TonePlayer.js
@@ -7,9 +7,9 @@
* @license Distributed under the terms of the MIT License
*/
-import * as Tone from 'tone';
-import {SoundPlayer} from './SoundPlayer';
-
+import * as Tone from "tone";
+import { isNumeric } from "../util/Util.js";
+import { SoundPlayer } from "./SoundPlayer.js";
/**
* This class handles the playing of tones.
@@ -27,23 +27,23 @@ import {SoundPlayer} from './SoundPlayer';
export class TonePlayer extends SoundPlayer
{
constructor({
- psychoJS,
- note = 'C4',
- duration_s = 0.5,
- volume = 1.0,
- loops = 0,
- soundLibrary = TonePlayer.SoundLibrary.TONE_JS,
- autoLog = true
- } = {})
+ psychoJS,
+ note = "C4",
+ duration_s = 0.5,
+ volume = 1.0,
+ loops = 0,
+ soundLibrary = TonePlayer.SoundLibrary.TONE_JS,
+ autoLog = true,
+ } = {})
{
super(psychoJS);
- this._addAttribute('note', note);
- this._addAttribute('duration_s', duration_s);
- this._addAttribute('volume', volume);
- this._addAttribute('loops', loops);
- this._addAttribute('soundLibrary', soundLibrary);
- this._addAttribute('autoLog', autoLog);
+ this._addAttribute("note", note);
+ this._addAttribute("duration_s", duration_s);
+ this._addAttribute("volume", volume);
+ this._addAttribute("loops", loops);
+ this._addAttribute("soundLibrary", soundLibrary);
+ this._addAttribute("autoLog", autoLog);
// initialise the sound library:
this._initSoundLibrary();
@@ -57,7 +57,6 @@ export class TonePlayer extends SoundPlayer
}
}
-
/**
* Determine whether this player can play the given sound.
*
@@ -74,39 +73,39 @@ export class TonePlayer extends SoundPlayer
static accept(sound)
{
// if the sound's value is an integer, we interpret it as a frequency:
- if ($.isNumeric(sound.value))
+ if (isNumeric(sound.value))
{
return new TonePlayer({
psychoJS: sound.psychoJS,
note: sound.value,
duration_s: sound.secs,
volume: sound.volume,
- loops: sound.loops
+ loops: sound.loops,
});
}
// if the sound's value is a string, we check whether it is a note:
- if (typeof sound.value === 'string')
+ if (typeof sound.value === "string")
{
// mapping between the PsychoPY notes and the standard ones:
let psychopyToToneMap = new Map();
- for (const note of ['A', 'B', 'C', 'D', 'E', 'F', 'G'])
+ for (const note of ["A", "B", "C", "D", "E", "F", "G"])
{
psychopyToToneMap.set(note, note);
- psychopyToToneMap.set(note + 'fl', note + 'b');
- psychopyToToneMap.set(note + 'sh', note + '#');
+ psychopyToToneMap.set(note + "fl", note + "b");
+ psychopyToToneMap.set(note + "sh", note + "#");
}
// check whether the sound's value is a recognised note:
const note = psychopyToToneMap.get(sound.value);
- if (typeof note !== 'undefined')
+ if (typeof note !== "undefined")
{
return new TonePlayer({
psychoJS: sound.psychoJS,
note: note + sound.octave,
duration_s: sound.secs,
volume: sound.volume,
- loops: sound.loops
+ loops: sound.loops,
});
}
}
@@ -115,7 +114,6 @@ export class TonePlayer extends SoundPlayer
return undefined;
}
-
/**
* Get the duration of the sound.
*
@@ -129,7 +127,6 @@ export class TonePlayer extends SoundPlayer
return this.duration_s;
}
-
/**
* Set the duration of the tone.
*
@@ -143,7 +140,6 @@ export class TonePlayer extends SoundPlayer
this.duration_s = duration_s;
}
-
/**
* Set the number of loops.
*
@@ -157,7 +153,6 @@ export class TonePlayer extends SoundPlayer
this._loops = loops;
}
-
/**
* Set the volume of the tone.
*
@@ -173,7 +168,7 @@ export class TonePlayer extends SoundPlayer
if (this._soundLibrary === TonePlayer.SoundLibrary.TONE_JS)
{
- if (typeof this._volumeNode !== 'undefined')
+ if (typeof this._volumeNode !== "undefined")
{
this._volumeNode.mute = mute;
this._volumeNode.volume.value = -60 + volume * 66;
@@ -190,7 +185,6 @@ export class TonePlayer extends SoundPlayer
}
}
-
/**
* Start playing the sound.
*
@@ -201,7 +195,7 @@ export class TonePlayer extends SoundPlayer
*/
play(loops)
{
- if (typeof loops !== 'undefined')
+ if (typeof loops !== "undefined")
{
this._loops = loops;
}
@@ -222,7 +216,7 @@ export class TonePlayer extends SoundPlayer
playToneCallback = () =>
{
self._webAudioOscillator = self._audioContext.createOscillator();
- self._webAudioOscillator.type = 'sine';
+ self._webAudioOscillator.type = "sine";
self._webAudioOscillator.frequency.value = 440;
self._webAudioOscillator.connect(self._audioContext.destination);
const contextCurrentTime = self._audioContext.currentTime;
@@ -236,7 +230,6 @@ export class TonePlayer extends SoundPlayer
{
playToneCallback();
}
-
// repeat forever:
else if (this.loops === -1)
{
@@ -244,22 +237,21 @@ export class TonePlayer extends SoundPlayer
playToneCallback,
this.duration_s,
Tone.now(),
- Infinity
+ Infinity,
);
}
- else
// repeat this._loops times:
+ else
{
this._toneId = Tone.Transport.scheduleRepeat(
playToneCallback,
this.duration_s,
Tone.now(),
- this.duration_s * (this._loops + 1)
+ this.duration_s * (this._loops + 1),
);
}
}
-
/**
* Stop playing the sound immediately.
*
@@ -287,7 +279,6 @@ export class TonePlayer extends SoundPlayer
}
}
-
/**
* Initialise the sound library.
*
@@ -301,24 +292,24 @@ export class TonePlayer extends SoundPlayer
_initSoundLibrary()
{
const response = {
- origin: 'TonePlayer._initSoundLibrary',
- context: 'when initialising the sound library'
+ origin: "TonePlayer._initSoundLibrary",
+ context: "when initialising the sound library",
};
if (this._soundLibrary === TonePlayer.SoundLibrary.TONE_JS)
{
// check that Tone.js is available:
- if (typeof Tone === 'undefined')
+ if (typeof Tone === "undefined")
{
throw Object.assign(response, {
- error: "Tone.js is not available. A different sound library must be selected. Please contact the experiment designer."
+ error: "Tone.js is not available. A different sound library must be selected. Please contact the experiment designer.",
});
}
// start the Tone Transport if it has not started already:
- if (typeof Tone !== 'undefined' && Tone.Transport.state !== 'started')
+ if (typeof Tone !== "undefined" && Tone.Transport.state !== "started")
{
- this.psychoJS.logger.info('[PsychoJS] start Tone Transport');
+ this.psychoJS.logger.info("[PsychoJS] start Tone Transport");
Tone.Transport.start(Tone.now());
// this is necessary to prevent Tone from introducing a delay when triggering a note
@@ -329,14 +320,14 @@ export class TonePlayer extends SoundPlayer
// create a synth: we use a triangular oscillator with hardly any envelope:
this._synthOtions = {
oscillator: {
- type: 'square' //'triangle'
+ type: "square", // 'triangle'
},
envelope: {
attack: 0.001, // 1ms
- decay: 0.001, // 1ms
+ decay: 0.001, // 1ms
sustain: 1,
- release: 0.001 // 1ms
- }
+ release: 0.001, // 1ms
+ },
};
this._synth = new Tone.Synth(this._synthOtions);
@@ -345,7 +336,7 @@ export class TonePlayer extends SoundPlayer
this._synth.connect(this._volumeNode);
// connect the volume node to the master output:
- if (typeof this._volumeNode.toDestination === 'function')
+ if (typeof this._volumeNode.toDestination === "function")
{
this._volumeNode.toDestination();
}
@@ -357,15 +348,15 @@ export class TonePlayer extends SoundPlayer
else
{
// create an AudioContext:
- if (typeof this._audioContext === 'undefined')
+ if (typeof this._audioContext === "undefined")
{
const AudioContext = window.AudioContext || window.webkitAudioContext;
// if AudioContext is not available (e.g. on IE), we throw an exception:
- if (typeof AudioContext === 'undefined')
+ if (typeof AudioContext === "undefined")
{
throw Object.assign(response, {
- error: `AudioContext is not available on your browser, ${this._psychoJS.browser}, please contact the experiment designer.`
+ error: `AudioContext is not available on your browser, ${this._psychoJS.browser}, please contact the experiment designer.`,
});
}
@@ -373,15 +364,13 @@ export class TonePlayer extends SoundPlayer
}
}
}
-
}
-
/**
*
* @type {{TONE_JS: *, AUDIO_CONTEXT: *}}
*/
TonePlayer.SoundLibrary = {
- AUDIO_CONTEXT: Symbol.for('AUDIO_CONTEXT'),
- TONE_JS: Symbol.for('TONE_JS')
+ AUDIO_CONTEXT: Symbol.for("AUDIO_CONTEXT"),
+ TONE_JS: Symbol.for("TONE_JS"),
};
diff --git a/src/sound/TrackPlayer.js b/src/sound/TrackPlayer.js
index 058d481..a5dcbda 100644
--- a/src/sound/TrackPlayer.js
+++ b/src/sound/TrackPlayer.js
@@ -7,8 +7,7 @@
* @license Distributed under the terms of the MIT License
*/
-import {SoundPlayer} from './SoundPlayer';
-
+import { SoundPlayer } from "./SoundPlayer.js";
/**
* This class handles the playback of sound tracks.
@@ -30,28 +29,27 @@ import {SoundPlayer} from './SoundPlayer';
export class TrackPlayer extends SoundPlayer
{
constructor({
- psychoJS,
- howl,
- startTime = 0,
- stopTime = -1,
- stereo = true,
- volume = 0,
- loops = 0
- } = {})
+ psychoJS,
+ howl,
+ startTime = 0,
+ stopTime = -1,
+ stereo = true,
+ volume = 0,
+ loops = 0,
+ } = {})
{
super(psychoJS);
- this._addAttribute('howl', howl);
- this._addAttribute('startTime', startTime);
- this._addAttribute('stopTime', stopTime);
- this._addAttribute('stereo', stereo);
- this._addAttribute('loops', loops);
- this._addAttribute('volume', volume);
+ this._addAttribute("howl", howl);
+ this._addAttribute("startTime", startTime);
+ this._addAttribute("stopTime", stopTime);
+ this._addAttribute("stereo", stereo);
+ this._addAttribute("loops", loops);
+ this._addAttribute("volume", volume);
this._currentLoopIndex = -1;
}
-
/**
* Determine whether this player can play the given sound.
*
@@ -66,10 +64,10 @@ export class TrackPlayer extends SoundPlayer
static accept(sound)
{
// if the sound's value is a string, we check whether it is the name of a resource:
- if (typeof sound.value === 'string')
+ if (typeof sound.value === "string")
{
const howl = sound.psychoJS.serverManager.getResource(sound.value);
- if (typeof howl !== 'undefined')
+ if (typeof howl !== "undefined")
{
// build the player:
const player = new TrackPlayer({
@@ -79,7 +77,7 @@ export class TrackPlayer extends SoundPlayer
stopTime: sound.stopTime,
stereo: sound.stereo,
loops: sound.loops,
- volume: sound.volume
+ volume: sound.volume,
});
return player;
}
@@ -89,7 +87,6 @@ export class TrackPlayer extends SoundPlayer
return undefined;
}
-
/**
* Get the duration of the sound, in seconds.
*
@@ -103,7 +100,6 @@ export class TrackPlayer extends SoundPlayer
return this._howl.duration();
}
-
/**
* Set the duration of the track.
*
@@ -114,14 +110,13 @@ export class TrackPlayer extends SoundPlayer
*/
setDuration(duration_s)
{
- if (typeof this._howl !== 'undefined')
+ if (typeof this._howl !== "undefined")
{
// Unfortunately Howler.js provides duration setting method
this._howl._duration = duration_s;
}
}
-
/**
* Set the volume of the tone.
*
@@ -139,7 +134,6 @@ export class TrackPlayer extends SoundPlayer
this._howl.mute(mute);
}
-
/**
* Set the number of loops.
*
@@ -163,7 +157,6 @@ export class TrackPlayer extends SoundPlayer
}
}
-
/**
* Start playing the sound.
*
@@ -175,7 +168,7 @@ export class TrackPlayer extends SoundPlayer
*/
play(loops, fadeDuration = 17)
{
- if (typeof loops !== 'undefined')
+ if (typeof loops !== "undefined")
{
this.setLoops(loops);
}
@@ -184,7 +177,7 @@ export class TrackPlayer extends SoundPlayer
if (loops > 0)
{
const self = this;
- this._howl.on('end', (event) =>
+ this._howl.on("end", (event) =>
{
++this._currentLoopIndex;
if (self._currentLoopIndex > self._loops)
@@ -205,7 +198,6 @@ export class TrackPlayer extends SoundPlayer
this._howl.fade(0, this._volume, fadeDuration, this._id);
}
-
/**
* Stop playing the sound immediately.
*
@@ -216,11 +208,11 @@ export class TrackPlayer extends SoundPlayer
*/
stop(fadeDuration = 17)
{
- this._howl.once('fade', (id) => {
+ this._howl.once("fade", (id) =>
+ {
this._howl.stop(id);
- this._howl.off('end');
+ this._howl.off("end");
});
this._howl.fade(this._howl.volume(), 0, fadeDuration, this._id);
}
-
}
diff --git a/src/sound/index.js b/src/sound/index.js
index 6cec21e..81637d7 100644
--- a/src/sound/index.js
+++ b/src/sound/index.js
@@ -1,9 +1,9 @@
-export * from './Sound.js';
-export * from './SoundPlayer.js';
-export * from './TonePlayer.js';
-export * from './TrackPlayer.js';
+export * from "./Sound.js";
+export * from "./SoundPlayer.js";
+export * from "./TonePlayer.js";
+export * from "./TrackPlayer.js";
-export * from './Microphone.js';
-export * from './AudioClip.js';
-export * from './AudioClipPlayer.js';
-//export * from './Transcriber.js';
+export * from "./AudioClip.js";
+export * from "./AudioClipPlayer.js";
+export * from "./Microphone.js";
+// export * from './Transcriber.js';
diff --git a/src/util/Clock.js b/src/util/Clock.js
index 4ee213b..5259a46 100644
--- a/src/util/Clock.js
+++ b/src/util/Clock.js
@@ -7,7 +7,6 @@
* @license Distributed under the terms of the MIT License
*/
-
/**
* MonotonicClock offers a convenient way to keep track of time during experiments. An experiment can have as many independent clocks as needed, e.g. one to time responses, another one to keep track of stimuli, etc.
*
@@ -22,7 +21,6 @@ export class MonotonicClock
this._timeAtLastReset = startTime;
}
-
/**
* Get the current time on this clock.
*
@@ -36,7 +34,6 @@ export class MonotonicClock
return MonotonicClock.getReferenceTime() - this._timeAtLastReset;
}
-
/**
* Get the current offset being applied to the high resolution timebase used by this Clock.
*
@@ -50,7 +47,6 @@ export class MonotonicClock
return this._timeAtLastReset;
}
-
/**
* Get the time elapsed since the reference point.
*
@@ -65,7 +61,6 @@ export class MonotonicClock
// return (new Date().getTime()) / 1000.0 - MonotonicClock._referenceTime;
}
-
/**
* Get the current timestamp with language-sensitive formatting rules applied.
*
@@ -79,18 +74,18 @@ export class MonotonicClock
* @param {object} options - An object with detailed date and time styling information.
* @return {string} The current timestamp in the chosen format.
*/
- static getDate(locales = 'en-CA', optionsMaybe)
+ static getDate(locales = "en-CA", optionsMaybe)
{
const date = new Date();
const options = Object.assign({
hour12: false,
- year: 'numeric',
- month: '2-digit',
- day: '2-digit',
- hour: 'numeric',
- minute: 'numeric',
- second: 'numeric',
- fractionalSecondDigits: 3
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric",
+ fractionalSecondDigits: 3,
}, optionsMaybe);
const dateTimeFormat = new Intl.DateTimeFormat(locales, options);
@@ -114,15 +109,14 @@ export class MonotonicClock
// yyyy-mm-dd, hh:mm:ss.sss
return MonotonicClock.getDate()
// yyyy-mm-dd_hh:mm:ss.sss
- .replace(', ', '_')
+ .replace(", ", "_")
// yyyy-mm-dd_hh[h]mm:ss.sss
- .replace(':', 'h')
+ .replace(":", "h")
// yyyy-mm-dd_hh[h]mm.ss.sss
- .replace(':', '.');
+ .replace(":", ".");
}
}
-
/**
* The clock's referenceTime is the time when the module was loaded (in seconds).
*
@@ -135,7 +129,6 @@ MonotonicClock._referenceTime = performance.now() / 1000.0;
// MonotonicClock._referenceTime = new Date().getTime() / 1000.0;
-
/**
* Clock is a MonotonicClock that also offers the possibility of being reset.
*
@@ -164,7 +157,6 @@ export class Clock extends MonotonicClock
this._timeAtLastReset = MonotonicClock.getReferenceTime() + newTime;
}
-
/**
* Add more time to the clock's 'start' time (t0).
*
@@ -182,7 +174,6 @@ export class Clock extends MonotonicClock
}
}
-
/**
* CountdownTimer is a clock counts down from the time of last reset.This class handles multiple color spaces, and offers various
* static methods for converting colors from one space to another.
@@ -32,27 +31,26 @@
*/
export class Color
{
-
- constructor(obj = 'black', colorspace = Color.COLOR_SPACE.RGB)
+ constructor(obj = "black", colorspace = Color.COLOR_SPACE.RGB)
{
const response = {
- origin: 'Color',
- context: 'when defining a color'
+ origin: "Color",
+ context: "when defining a color",
};
// named color (e.g. 'seagreen') or string hexadecimal representation (e.g. '#FF0000'):
// note: we expect the color space to be RGB
- if (typeof obj == 'string')
+ if (typeof obj == "string")
{
if (colorspace !== Color.COLOR_SPACE.RGB)
{
throw Object.assign(response, {
- error: 'the colorspace must be RGB for a named color'
+ error: "the colorspace must be RGB for a named color",
});
}
// hexademical representation:
- if (obj[0] === '#')
+ if (obj[0] === "#")
{
this._hex = obj;
}
@@ -61,7 +59,7 @@ export class Color
{
if (!(obj.toLowerCase() in Color.NAMED_COLORS))
{
- throw Object.assign(response, {error: 'unknown named color: ' + obj});
+ throw Object.assign(response, { error: "unknown named color: " + obj });
}
this._hex = Color.NAMED_COLORS[obj.toLowerCase()];
@@ -69,23 +67,21 @@ export class Color
this._rgb = Color.hexToRgb(this._hex);
}
-
// hexadecimal number representation (e.g. 0xFF0000)
// note: we expect the color space to be RGB
- else if (typeof obj == 'number')
+ else if (typeof obj == "number")
{
if (colorspace !== Color.COLOR_SPACE.RGB)
{
throw Object.assign(response, {
- error: 'the colorspace must be RGB for' +
- ' a' +
- ' named color'
+ error: "the colorspace must be RGB for"
+ + " a"
+ + " named color",
});
}
this._rgb = Color._intToRgb(obj);
}
-
// array of numbers:
else if (Array.isArray(obj))
{
@@ -124,17 +120,15 @@ export class Color
break;
default:
- throw Object.assign(response, {error: 'unknown colorspace: ' + colorspace});
+ throw Object.assign(response, { error: "unknown colorspace: " + colorspace });
}
}
-
else if (obj instanceof Color)
{
this._rgb = obj._rgb.slice();
}
}
-
/**
* Get the [0,1] RGB triplet equivalent of this Color.
*
@@ -148,7 +142,6 @@ export class Color
return this._rgb;
}
-
/**
* Get the [0,255] RGB triplet equivalent of this Color.
*
@@ -162,7 +155,6 @@ export class Color
return [Math.round(this._rgb[0] * 255.0), Math.round(this._rgb[1] * 255.0), Math.round(this._rgb[2] * 255.0)];
}
-
/**
* Get the hexadecimal color code equivalent of this Color.
*
@@ -173,7 +165,7 @@ export class Color
*/
get hex()
{
- if (typeof this._hex === 'undefined')
+ if (typeof this._hex === "undefined")
{
this._hex = Color._rgbToHex(this._rgb);
}
@@ -190,14 +182,13 @@ export class Color
*/
get int()
{
- if (typeof this._int === 'undefined')
+ if (typeof this._int === "undefined")
{
this._int = Color._rgbToInt(this._rgb);
}
return this._int;
}
-
/*
get hsv() {
if (typeof this._hsv === 'undefined')
@@ -216,7 +207,6 @@ export class Color
}
*/
-
/**
* String representation of the color, i.e. the hexadecimal representation.
*
@@ -230,7 +220,6 @@ export class Color
return this.hex;
}
-
/**
* Get the [0,255] RGB triplet equivalent of the hexadecimal color code.
*
@@ -247,16 +236,15 @@ export class Color
if (result == null)
{
throw {
- origin: 'Color.hexToRgb255',
- context: 'when converting an hexadecimal color code to its 255- or [0,1]-based RGB color representation',
- error: 'unable to parse the argument: wrong type or wrong code'
+ origin: "Color.hexToRgb255",
+ context: "when converting an hexadecimal color code to its 255- or [0,1]-based RGB color representation",
+ error: "unable to parse the argument: wrong type or wrong code",
};
}
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
}
-
/**
* Get the [0,1] RGB triplet equivalent of the hexadecimal color code.
*
@@ -273,7 +261,6 @@ export class Color
return [r255 / 255.0, g255 / 255.0, b255 / 255.0];
}
-
/**
* Get the hexadecimal color code equivalent of the [0, 255] RGB triplet.
*
@@ -287,8 +274,8 @@ export class Color
static rgb255ToHex(rgb255)
{
const response = {
- origin: 'Color.rgb255ToHex',
- context: 'when converting an rgb triplet to its hexadecimal color representation'
+ origin: "Color.rgb255ToHex",
+ context: "when converting an rgb triplet to its hexadecimal color representation",
};
try
@@ -298,11 +285,10 @@ export class Color
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
/**
* Get the hexadecimal color code equivalent of the [0, 1] RGB triplet.
*
@@ -316,8 +302,8 @@ export class Color
static rgbToHex(rgb)
{
const response = {
- origin: 'Color.rgbToHex',
- context: 'when converting an rgb triplet to its hexadecimal color representation'
+ origin: "Color.rgbToHex",
+ context: "when converting an rgb triplet to its hexadecimal color representation",
};
try
@@ -327,11 +313,10 @@ export class Color
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
/**
* Get the integer equivalent of the [0, 1] RGB triplet.
*
@@ -345,8 +330,8 @@ export class Color
static rgbToInt(rgb)
{
const response = {
- origin: 'Color.rgbToInt',
- context: 'when converting an rgb triplet to its integer representation'
+ origin: "Color.rgbToInt",
+ context: "when converting an rgb triplet to its integer representation",
};
try
@@ -356,11 +341,10 @@ export class Color
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
/**
* Get the integer equivalent of the [0, 255] RGB triplet.
*
@@ -374,8 +358,8 @@ export class Color
static rgb255ToInt(rgb255)
{
const response = {
- origin: 'Color.rgb255ToInt',
- context: 'when converting an rgb triplet to its integer representation'
+ origin: "Color.rgb255ToInt",
+ context: "when converting an rgb triplet to its integer representation",
};
try
{
@@ -384,11 +368,10 @@ export class Color
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
/**
* Get the hexadecimal color code equivalent of the [0, 255] RGB triplet.
*
@@ -406,7 +389,6 @@ export class Color
return "#" + ((1 << 24) + (rgb255[0] << 16) + (rgb255[1] << 8) + rgb255[2]).toString(16).slice(1);
}
-
/**
* Get the hexadecimal color code equivalent of the [0, 1] RGB triplet.
*
@@ -425,7 +407,6 @@ export class Color
return Color._rgb255ToHex(rgb255);
}
-
/**
* Get the integer equivalent of the [0, 1] RGB triplet.
*
@@ -444,7 +425,6 @@ export class Color
return Color._rgb255ToInt(rgb255);
}
-
/**
* Get the integer equivalent of the [0, 255] RGB triplet.
*
@@ -462,7 +442,6 @@ export class Color
return rgb255[0] * 0x10000 + rgb255[1] * 0x100 + rgb255[2];
}
-
/**
* Get the [0, 255] based RGB triplet equivalent of the integer color code.
*
@@ -484,7 +463,6 @@ export class Color
return [r255, g255, b255];
}
-
/**
* Get the [0, 1] based RGB triplet equivalent of the integer color code.
*
@@ -517,20 +495,21 @@ export class Color
*/
static _checkTypeAndRange(arg, range = undefined)
{
- if (!Array.isArray(arg) || arg.length !== 3 ||
- typeof arg[0] !== 'number' || typeof arg[1] !== 'number' || typeof arg[2] !== 'number')
+ if (
+ !Array.isArray(arg) || arg.length !== 3
+ || typeof arg[0] !== "number" || typeof arg[1] !== "number" || typeof arg[2] !== "number"
+ )
{
- throw 'the argument should be an array of numbers of length 3';
+ throw "the argument should be an array of numbers of length 3";
}
- if (typeof range !== 'undefined' && (arg[0] < range[0] || arg[0] > range[1] || arg[1] < range[0] || arg[1] > range[1] || arg[2] < range[0] || arg[2] > range[1]))
+ if (typeof range !== "undefined" && (arg[0] < range[0] || arg[0] > range[1] || arg[1] < range[0] || arg[1] > range[1] || arg[2] < range[0] || arg[2] > range[1]))
{
- throw 'the color components should all belong to [' + range[0] + ', ' + range[1] + ']';
+ throw "the color components should all belong to [" + range[0] + ", " + range[1] + "]";
}
}
}
-
/**
* Color spaces.
*
@@ -543,13 +522,12 @@ Color.COLOR_SPACE = {
/**
* RGB colorspace: [r,g,b] with r,g,b in [-1, 1]
*/
- RGB: Symbol.for('RGB'),
+ RGB: Symbol.for("RGB"),
/**
* RGB255 colorspace: [r,g,b] with r,g,b in [0, 255]
*/
- RGB255: Symbol.for('RGB255'),
-
+ RGB255: Symbol.for("RGB255"),
/*
HSV: Symbol.for('HSV'),
DKL: Symbol.for('DKL'),
@@ -557,7 +535,6 @@ Color.COLOR_SPACE = {
*/
};
-
/**
* Named colors.
*
@@ -567,151 +544,151 @@ Color.COLOR_SPACE = {
* @public
*/
Color.NAMED_COLORS = {
- 'aliceblue': '#F0F8FF',
- 'antiquewhite': '#FAEBD7',
- 'aqua': '#00FFFF',
- 'aquamarine': '#7FFFD4',
- 'azure': '#F0FFFF',
- 'beige': '#F5F5DC',
- 'bisque': '#FFE4C4',
- 'black': '#000000',
- 'blanchedalmond': '#FFEBCD',
- 'blue': '#0000FF',
- 'blueviolet': '#8A2BE2',
- 'brown': '#A52A2A',
- 'burlywood': '#DEB887',
- 'cadetblue': '#5F9EA0',
- 'chartreuse': '#7FFF00',
- 'chocolate': '#D2691E',
- 'coral': '#FF7F50',
- 'cornflowerblue': '#6495ED',
- 'cornsilk': '#FFF8DC',
- 'crimson': '#DC143C',
- 'cyan': '#00FFFF',
- 'darkblue': '#00008B',
- 'darkcyan': '#008B8B',
- 'darkgoldenrod': '#B8860B',
- 'darkgray': '#A9A9A9',
- 'darkgrey': '#A9A9A9',
- 'darkgreen': '#006400',
- 'darkkhaki': '#BDB76B',
- 'darkmagenta': '#8B008B',
- 'darkolivegreen': '#556B2F',
- 'darkorange': '#FF8C00',
- 'darkorchid': '#9932CC',
- 'darkred': '#8B0000',
- 'darksalmon': '#E9967A',
- 'darkseagreen': '#8FBC8B',
- 'darkslateblue': '#483D8B',
- 'darkslategray': '#2F4F4F',
- 'darkslategrey': '#2F4F4F',
- 'darkturquoise': '#00CED1',
- 'darkviolet': '#9400D3',
- 'deeppink': '#FF1493',
- 'deepskyblue': '#00BFFF',
- 'dimgray': '#696969',
- 'dimgrey': '#696969',
- 'dodgerblue': '#1E90FF',
- 'firebrick': '#B22222',
- 'floralwhite': '#FFFAF0',
- 'forestgreen': '#228B22',
- 'fuchsia': '#FF00FF',
- 'gainsboro': '#DCDCDC',
- 'ghostwhite': '#F8F8FF',
- 'gold': '#FFD700',
- 'goldenrod': '#DAA520',
- 'gray': '#808080',
- 'grey': '#808080',
- 'green': '#008000',
- 'greenyellow': '#ADFF2F',
- 'honeydew': '#F0FFF0',
- 'hotpink': '#FF69B4',
- 'indianred': '#CD5C5C',
- 'indigo': '#4B0082',
- 'ivory': '#FFFFF0',
- 'khaki': '#F0E68C',
- 'lavender': '#E6E6FA',
- 'lavenderblush': '#FFF0F5',
- 'lawngreen': '#7CFC00',
- 'lemonchiffon': '#FFFACD',
- 'lightblue': '#ADD8E6',
- 'lightcoral': '#F08080',
- 'lightcyan': '#E0FFFF',
- 'lightgoldenrodyellow': '#FAFAD2',
- 'lightgray': '#D3D3D3',
- 'lightgrey': '#D3D3D3',
- 'lightgreen': '#90EE90',
- 'lightpink': '#FFB6C1',
- 'lightsalmon': '#FFA07A',
- 'lightseagreen': '#20B2AA',
- 'lightskyblue': '#87CEFA',
- 'lightslategray': '#778899',
- 'lightslategrey': '#778899',
- 'lightsteelblue': '#B0C4DE',
- 'lightyellow': '#FFFFE0',
- 'lime': '#00FF00',
- 'limegreen': '#32CD32',
- 'linen': '#FAF0E6',
- 'magenta': '#FF00FF',
- 'maroon': '#800000',
- 'mediumaquamarine': '#66CDAA',
- 'mediumblue': '#0000CD',
- 'mediumorchid': '#BA55D3',
- 'mediumpurple': '#9370DB',
- 'mediumseagreen': '#3CB371',
- 'mediumslateblue': '#7B68EE',
- 'mediumspringgreen': '#00FA9A',
- 'mediumturquoise': '#48D1CC',
- 'mediumvioletred': '#C71585',
- 'midnightblue': '#191970',
- 'mintcream': '#F5FFFA',
- 'mistyrose': '#FFE4E1',
- 'moccasin': '#FFE4B5',
- 'navajowhite': '#FFDEAD',
- 'navy': '#000080',
- 'oldlace': '#FDF5E6',
- 'olive': '#808000',
- 'olivedrab': '#6B8E23',
- 'orange': '#FFA500',
- 'orangered': '#FF4500',
- 'orchid': '#DA70D6',
- 'palegoldenrod': '#EEE8AA',
- 'palegreen': '#98FB98',
- 'paleturquoise': '#AFEEEE',
- 'palevioletred': '#DB7093',
- 'papayawhip': '#FFEFD5',
- 'peachpuff': '#FFDAB9',
- 'peru': '#CD853F',
- 'pink': '#FFC0CB',
- 'plum': '#DDA0DD',
- 'powderblue': '#B0E0E6',
- 'purple': '#800080',
- 'red': '#FF0000',
- 'rosybrown': '#BC8F8F',
- 'royalblue': '#4169E1',
- 'saddlebrown': '#8B4513',
- 'salmon': '#FA8072',
- 'sandybrown': '#F4A460',
- 'seagreen': '#2E8B57',
- 'seashell': '#FFF5EE',
- 'sienna': '#A0522D',
- 'silver': '#C0C0C0',
- 'skyblue': '#87CEEB',
- 'slateblue': '#6A5ACD',
- 'slategray': '#708090',
- 'slategrey': '#708090',
- 'snow': '#FFFAFA',
- 'springgreen': '#00FF7F',
- 'steelblue': '#4682B4',
- 'tan': '#D2B48C',
- 'teal': '#008080',
- 'thistle': '#D8BFD8',
- 'tomato': '#FF6347',
- 'turquoise': '#40E0D0',
- 'violet': '#EE82EE',
- 'wheat': '#F5DEB3',
- 'white': '#FFFFFF',
- 'whitesmoke': '#F5F5F5',
- 'yellow': '#FFFF00',
- 'yellowgreen': '#9ACD32'
+ "aliceblue": "#F0F8FF",
+ "antiquewhite": "#FAEBD7",
+ "aqua": "#00FFFF",
+ "aquamarine": "#7FFFD4",
+ "azure": "#F0FFFF",
+ "beige": "#F5F5DC",
+ "bisque": "#FFE4C4",
+ "black": "#000000",
+ "blanchedalmond": "#FFEBCD",
+ "blue": "#0000FF",
+ "blueviolet": "#8A2BE2",
+ "brown": "#A52A2A",
+ "burlywood": "#DEB887",
+ "cadetblue": "#5F9EA0",
+ "chartreuse": "#7FFF00",
+ "chocolate": "#D2691E",
+ "coral": "#FF7F50",
+ "cornflowerblue": "#6495ED",
+ "cornsilk": "#FFF8DC",
+ "crimson": "#DC143C",
+ "cyan": "#00FFFF",
+ "darkblue": "#00008B",
+ "darkcyan": "#008B8B",
+ "darkgoldenrod": "#B8860B",
+ "darkgray": "#A9A9A9",
+ "darkgrey": "#A9A9A9",
+ "darkgreen": "#006400",
+ "darkkhaki": "#BDB76B",
+ "darkmagenta": "#8B008B",
+ "darkolivegreen": "#556B2F",
+ "darkorange": "#FF8C00",
+ "darkorchid": "#9932CC",
+ "darkred": "#8B0000",
+ "darksalmon": "#E9967A",
+ "darkseagreen": "#8FBC8B",
+ "darkslateblue": "#483D8B",
+ "darkslategray": "#2F4F4F",
+ "darkslategrey": "#2F4F4F",
+ "darkturquoise": "#00CED1",
+ "darkviolet": "#9400D3",
+ "deeppink": "#FF1493",
+ "deepskyblue": "#00BFFF",
+ "dimgray": "#696969",
+ "dimgrey": "#696969",
+ "dodgerblue": "#1E90FF",
+ "firebrick": "#B22222",
+ "floralwhite": "#FFFAF0",
+ "forestgreen": "#228B22",
+ "fuchsia": "#FF00FF",
+ "gainsboro": "#DCDCDC",
+ "ghostwhite": "#F8F8FF",
+ "gold": "#FFD700",
+ "goldenrod": "#DAA520",
+ "gray": "#808080",
+ "grey": "#808080",
+ "green": "#008000",
+ "greenyellow": "#ADFF2F",
+ "honeydew": "#F0FFF0",
+ "hotpink": "#FF69B4",
+ "indianred": "#CD5C5C",
+ "indigo": "#4B0082",
+ "ivory": "#FFFFF0",
+ "khaki": "#F0E68C",
+ "lavender": "#E6E6FA",
+ "lavenderblush": "#FFF0F5",
+ "lawngreen": "#7CFC00",
+ "lemonchiffon": "#FFFACD",
+ "lightblue": "#ADD8E6",
+ "lightcoral": "#F08080",
+ "lightcyan": "#E0FFFF",
+ "lightgoldenrodyellow": "#FAFAD2",
+ "lightgray": "#D3D3D3",
+ "lightgrey": "#D3D3D3",
+ "lightgreen": "#90EE90",
+ "lightpink": "#FFB6C1",
+ "lightsalmon": "#FFA07A",
+ "lightseagreen": "#20B2AA",
+ "lightskyblue": "#87CEFA",
+ "lightslategray": "#778899",
+ "lightslategrey": "#778899",
+ "lightsteelblue": "#B0C4DE",
+ "lightyellow": "#FFFFE0",
+ "lime": "#00FF00",
+ "limegreen": "#32CD32",
+ "linen": "#FAF0E6",
+ "magenta": "#FF00FF",
+ "maroon": "#800000",
+ "mediumaquamarine": "#66CDAA",
+ "mediumblue": "#0000CD",
+ "mediumorchid": "#BA55D3",
+ "mediumpurple": "#9370DB",
+ "mediumseagreen": "#3CB371",
+ "mediumslateblue": "#7B68EE",
+ "mediumspringgreen": "#00FA9A",
+ "mediumturquoise": "#48D1CC",
+ "mediumvioletred": "#C71585",
+ "midnightblue": "#191970",
+ "mintcream": "#F5FFFA",
+ "mistyrose": "#FFE4E1",
+ "moccasin": "#FFE4B5",
+ "navajowhite": "#FFDEAD",
+ "navy": "#000080",
+ "oldlace": "#FDF5E6",
+ "olive": "#808000",
+ "olivedrab": "#6B8E23",
+ "orange": "#FFA500",
+ "orangered": "#FF4500",
+ "orchid": "#DA70D6",
+ "palegoldenrod": "#EEE8AA",
+ "palegreen": "#98FB98",
+ "paleturquoise": "#AFEEEE",
+ "palevioletred": "#DB7093",
+ "papayawhip": "#FFEFD5",
+ "peachpuff": "#FFDAB9",
+ "peru": "#CD853F",
+ "pink": "#FFC0CB",
+ "plum": "#DDA0DD",
+ "powderblue": "#B0E0E6",
+ "purple": "#800080",
+ "red": "#FF0000",
+ "rosybrown": "#BC8F8F",
+ "royalblue": "#4169E1",
+ "saddlebrown": "#8B4513",
+ "salmon": "#FA8072",
+ "sandybrown": "#F4A460",
+ "seagreen": "#2E8B57",
+ "seashell": "#FFF5EE",
+ "sienna": "#A0522D",
+ "silver": "#C0C0C0",
+ "skyblue": "#87CEEB",
+ "slateblue": "#6A5ACD",
+ "slategray": "#708090",
+ "slategrey": "#708090",
+ "snow": "#FFFAFA",
+ "springgreen": "#00FF7F",
+ "steelblue": "#4682B4",
+ "tan": "#D2B48C",
+ "teal": "#008080",
+ "thistle": "#D8BFD8",
+ "tomato": "#FF6347",
+ "turquoise": "#40E0D0",
+ "violet": "#EE82EE",
+ "wheat": "#F5DEB3",
+ "white": "#FFFFFF",
+ "whitesmoke": "#F5F5F5",
+ "yellow": "#FFFF00",
+ "yellowgreen": "#9ACD32",
};
diff --git a/src/util/ColorMixin.js b/src/util/ColorMixin.js
index 59a31ca..e52207e 100644
--- a/src/util/ColorMixin.js
+++ b/src/util/ColorMixin.js
@@ -7,9 +7,7 @@
* @license Distributed under the terms of the MIT License
*/
-
-import {Color} from './Color';
-
+import { Color } from "./Color.js";
/**
* } color - the color
* @param {number} contrast - the contrast (must be between 0 and 1)
*/
- getContrastedColor(color, contrast)
- {
- const rgb = color.rgb.map(c => (c * 2.0 - 1.0) * contrast);
- return new Color(rgb, Color.COLOR_SPACE.RGB);
- }
-
-};
+ getContrastedColor(color, contrast)
+ {
+ const rgb = color.rgb.map((c) => (c * 2.0 - 1.0) * contrast);
+ return new Color(rgb, Color.COLOR_SPACE.RGB);
+ }
+ };
diff --git a/src/util/EventEmitter.js b/src/util/EventEmitter.js
index 5f4ad88..d0e6bff 100644
--- a/src/util/EventEmitter.js
+++ b/src/util/EventEmitter.js
@@ -7,9 +7,7 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as util from './Util';
-
+import * as util from "./Util.js";
/**
* EventEmitter implements the classic observer/observable pattern.
@@ -34,7 +32,6 @@ export class EventEmitter
this._onceUuids = new Map();
}
-
/**
* Listener called when this instance emits an event for which it is registered.
*
@@ -42,7 +39,6 @@ export class EventEmitter
* @param {object} data - the data passed to the listener
*/
-
/**
* Register a new listener for events with the given name emitted by this instance.
*
@@ -56,9 +52,9 @@ export class EventEmitter
on(name, listener)
{
// check that the listener is a function:
- if (typeof listener !== 'function')
+ if (typeof listener !== "function")
{
- throw new TypeError('listener must be a function');
+ throw new TypeError("listener must be a function");
}
// generate a new uuid:
@@ -69,12 +65,11 @@ export class EventEmitter
{
this._listeners.set(name, []);
}
- this._listeners.get(name).push({uuid, listener});
+ this._listeners.get(name).push({ uuid, listener });
return uuid;
}
-
/**
* Register a new listener for the given event name, and remove it as soon as the event has been emitted.
*
@@ -98,7 +93,6 @@ export class EventEmitter
return uuid;
}
-
/**
* Remove the listener with the given uuid associated to the given event name.
*
@@ -114,13 +108,12 @@ export class EventEmitter
if (relevantUuidListeners && relevantUuidListeners.length)
{
- this._listeners.set(name, relevantUuidListeners.filter(uuidlistener => (uuidlistener.uuid != uuid)));
+ this._listeners.set(name, relevantUuidListeners.filter((uuidlistener) => (uuidlistener.uuid != uuid)));
return true;
}
return false;
}
-
/**
* Emit an event with a given name and associated data.
*
@@ -138,11 +131,11 @@ export class EventEmitter
{
let onceUuids = this._onceUuids.get(name);
let self = this;
- relevantUuidListeners.forEach(({uuid, listener}) =>
+ relevantUuidListeners.forEach(({ uuid, listener }) =>
{
listener(data);
- if (typeof onceUuids !== 'undefined' && onceUuids.includes(uuid))
+ if (typeof onceUuids !== "undefined" && onceUuids.includes(uuid))
{
self.off(name, uuid);
}
@@ -152,6 +145,4 @@ export class EventEmitter
return false;
}
-
-
}
diff --git a/src/util/Pixi.js b/src/util/Pixi.js
new file mode 100644
index 0000000..b805768
--- /dev/null
+++ b/src/util/Pixi.js
@@ -0,0 +1,36 @@
+/**
+ * PIXI utilities.
+ *
+ * @authors Alain Pitiot, Sotiri Bakagiannis, Thomas Pronk
+ * @version 2021.2.0
+ * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2021 Open Science Tools Ltd. (https://opensciencetools.org)
+ * @license Distributed under the terms of the MIT License
+ */
+
+import * as PIXI from "pixi.js-legacy";
+import { to_px } from "./Util.js";
+
+/**
+ * Convert a position to a PIXI Point.
+ *
+ * @name module:util.to_pixiPoint
+ * @function
+ * @public
+ * @param {number[]} pos - the input position
+ * @param {string} posUnit - the position units
+ * @param {Window} win - the associated Window
+ * @param {boolean} [integerCoordinates = false] - whether or not to round the PIXI Point coordinates.
+ * @returns {number[]} the position as a PIXI Point
+ */
+export function to_pixiPoint(pos, posUnit, win, integerCoordinates = false)
+{
+ const pos_px = to_px(pos, posUnit, win);
+ if (integerCoordinates)
+ {
+ return new PIXI.Point(Math.round(pos_px[0]), Math.round(pos_px[1]));
+ }
+ else
+ {
+ return new PIXI.Point(pos_px[0], pos_px[1]);
+ }
+}
diff --git a/src/util/PsychObject.js b/src/util/PsychObject.js
index 381a6f9..019c947 100644
--- a/src/util/PsychObject.js
+++ b/src/util/PsychObject.js
@@ -8,10 +8,8 @@
* @license Distributed under the terms of the MIT License
*/
-
-import {EventEmitter} from './EventEmitter';
-import * as util from './Util';
-
+import { EventEmitter } from "./EventEmitter.js";
+import * as util from "./Util.js";
/**
* PsychoObject is the base class for all PsychoJS objects.
@@ -32,14 +30,13 @@ export class PsychObject extends EventEmitter
this._userAttributes = new Set();
// name:
- if (typeof name === 'undefined')
+ if (typeof name === "undefined")
{
name = this.constructor.name;
}
- this._addAttribute('name', name);
+ this._addAttribute("name", name);
}
-
/**
* Get the PsychoJS instance.
*
@@ -51,7 +48,6 @@ export class PsychObject extends EventEmitter
return this._psychoJS;
}
-
/**
* Setter for the PsychoJS attribute.
*
@@ -63,7 +59,6 @@ export class PsychObject extends EventEmitter
this._psychoJS = psychoJS;
}
-
/**
* String representation of the PsychObject.
*
@@ -74,38 +69,37 @@ export class PsychObject extends EventEmitter
*/
toString()
{
- let representation = this.constructor.name + '( ';
+ let representation = this.constructor.name + "( ";
let addComma = false;
for (const attribute of this._userAttributes)
{
if (addComma)
{
- representation += ', ';
+ representation += ", ";
}
addComma = true;
- let value = util.toString(this['_' + attribute]);
+ let value = util.toString(this["_" + attribute]);
const l = value.length;
if (l > 50)
{
- if (value[l - 1] === ')')
+ if (value[l - 1] === ")")
{
- value = value.substring(0, 50) + '~)';
+ value = value.substring(0, 50) + "~)";
}
else
{
- value = value.substring(0, 50) + '~';
+ value = value.substring(0, 50) + "~";
}
}
- representation += attribute + '=' + value;
+ representation += attribute + "=" + value;
}
- representation += ' )';
+ representation += " )";
return representation;
}
-
/**
* Set the value of an attribute.
*
@@ -121,31 +115,30 @@ export class PsychObject extends EventEmitter
_setAttribute(attributeName, attributeValue, log = false, operation = undefined, stealth = false)
{
const response = {
- origin: 'PsychObject.setAttribute',
- context: 'when setting the attribute of an object'
+ origin: "PsychObject.setAttribute",
+ context: "when setting the attribute of an object",
};
- if (typeof attributeName == 'undefined')
+ if (typeof attributeName == "undefined")
{
throw Object.assign(response, {
- error: 'the attribute name cannot be' +
- ' undefined'
+ error: "the attribute name cannot be"
+ + " undefined",
});
}
- if (typeof attributeValue == 'undefined')
+ if (typeof attributeValue == "undefined")
{
- this._psychoJS.logger.warn('setting the value of attribute: ' + attributeName + ' in PsychObject: ' + this._name + ' as: undefined');
+ this._psychoJS.logger.warn("setting the value of attribute: " + attributeName + " in PsychObject: " + this._name + " as: undefined");
}
// (*) apply operation to old and new values:
- if (typeof operation !== 'undefined' && this.hasOwnProperty('_' + attributeName))
+ if (typeof operation !== "undefined" && this.hasOwnProperty("_" + attributeName))
{
- let oldValue = this['_' + attributeName];
+ let oldValue = this["_" + attributeName];
// operations can only be applied to numbers and array of numbers (which can be empty):
- if (typeof attributeValue == 'number' || (Array.isArray(attributeValue) && (attributeValue.length === 0 || typeof attributeValue[0] == 'number')))
+ if (typeof attributeValue == "number" || (Array.isArray(attributeValue) && (attributeValue.length === 0 || typeof attributeValue[0] == "number")))
{
-
// value is an array:
if (Array.isArray(attributeValue))
{
@@ -155,160 +148,158 @@ export class PsychObject extends EventEmitter
if (attributeValue.length !== oldValue.length)
{
throw Object.assign(response, {
- error: 'old and new' +
- ' value should have' +
- ' the same size when they are both arrays'
+ error: "old and new"
+ + " value should have"
+ + " the same size when they are both arrays",
});
}
switch (operation)
{
- case '':
+ case "":
// no change to value;
break;
- case '+':
+ case "+":
attributeValue = attributeValue.map((v, i) => oldValue[i] + v);
break;
- case '*':
+ case "*":
attributeValue = attributeValue.map((v, i) => oldValue[i] * v);
break;
- case '-':
+ case "-":
attributeValue = attributeValue.map((v, i) => oldValue[i] - v);
break;
- case '/':
+ case "/":
attributeValue = attributeValue.map((v, i) => oldValue[i] / v);
break;
- case '**':
+ case "**":
attributeValue = attributeValue.map((v, i) => oldValue[i] ** v);
break;
- case '%':
+ case "%":
attributeValue = attributeValue.map((v, i) => oldValue[i] % v);
break;
default:
throw Object.assign(response, {
- error: 'unsupported' +
- ' operation: ' + operation + ' when setting: ' + attributeName + ' in: ' + this.name
+ error: "unsupported"
+ + " operation: " + operation + " when setting: " + attributeName + " in: " + this.name,
});
}
-
}
- else
// old value is a scalar
+ else
{
switch (operation)
{
- case '':
+ case "":
// no change to value;
break;
- case '+':
- attributeValue = attributeValue.map(v => oldValue + v);
+ case "+":
+ attributeValue = attributeValue.map((v) => oldValue + v);
break;
- case '*':
- attributeValue = attributeValue.map(v => oldValue * v);
+ case "*":
+ attributeValue = attributeValue.map((v) => oldValue * v);
break;
- case '-':
- attributeValue = attributeValue.map(v => oldValue - v);
+ case "-":
+ attributeValue = attributeValue.map((v) => oldValue - v);
break;
- case '/':
- attributeValue = attributeValue.map(v => oldValue / v);
+ case "/":
+ attributeValue = attributeValue.map((v) => oldValue / v);
break;
- case '**':
- attributeValue = attributeValue.map(v => oldValue ** v);
+ case "**":
+ attributeValue = attributeValue.map((v) => oldValue ** v);
break;
- case '%':
- attributeValue = attributeValue.map(v => oldValue % v);
+ case "%":
+ attributeValue = attributeValue.map((v) => oldValue % v);
break;
default:
throw Object.assign(response, {
- error: 'unsupported' +
- ' value: ' + JSON.stringify(attributeValue) + ' for' +
- ' operation: ' + operation + ' when setting: ' + attributeName + ' in: ' + this.name
+ error: "unsupported"
+ + " value: " + JSON.stringify(attributeValue) + " for"
+ + " operation: " + operation + " when setting: " + attributeName + " in: " + this.name,
});
}
}
}
- else
// value is a scalar
+ else
{
// old value is an array
if (Array.isArray(oldValue))
{
switch (operation)
{
- case '':
- attributeValue = oldValue.map(v => attributeValue);
+ case "":
+ attributeValue = oldValue.map((v) => attributeValue);
break;
- case '+':
- attributeValue = oldValue.map(v => v + attributeValue);
+ case "+":
+ attributeValue = oldValue.map((v) => v + attributeValue);
break;
- case '*':
- attributeValue = oldValue.map(v => v * attributeValue);
+ case "*":
+ attributeValue = oldValue.map((v) => v * attributeValue);
break;
- case '-':
- attributeValue = oldValue.map(v => v - attributeValue);
+ case "-":
+ attributeValue = oldValue.map((v) => v - attributeValue);
break;
- case '/':
- attributeValue = oldValue.map(v => v / attributeValue);
+ case "/":
+ attributeValue = oldValue.map((v) => v / attributeValue);
break;
- case '**':
- attributeValue = oldValue.map(v => v ** attributeValue);
+ case "**":
+ attributeValue = oldValue.map((v) => v ** attributeValue);
break;
- case '%':
- attributeValue = oldValue.map(v => v % attributeValue);
+ case "%":
+ attributeValue = oldValue.map((v) => v % attributeValue);
break;
default:
throw Object.assign(response, {
- error: 'unsupported' +
- ' operation: ' + operation + ' when setting: ' + attributeName + ' in: ' + this.name
+ error: "unsupported"
+ + " operation: " + operation + " when setting: " + attributeName + " in: " + this.name,
});
}
-
}
- else
// old value is a scalar
+ else
{
switch (operation)
{
- case '':
+ case "":
// no change to value;
break;
- case '+':
+ case "+":
attributeValue = oldValue + attributeValue;
break;
- case '*':
+ case "*":
attributeValue = oldValue * attributeValue;
break;
- case '-':
+ case "-":
attributeValue = oldValue - attributeValue;
break;
- case '/':
+ case "/":
attributeValue = oldValue / attributeValue;
break;
- case '**':
+ case "**":
attributeValue = oldValue ** attributeValue;
break;
- case '%':
+ case "%":
attributeValue = oldValue % attributeValue;
break;
default:
throw Object.assign(response, {
- error: 'unsupported' +
- ' value: ' + JSON.stringify(attributeValue) + ' for operation: ' + operation + ' when setting: ' + attributeName + ' in: ' + this.name
+ error: "unsupported"
+ + " value: " + JSON.stringify(attributeValue) + " for operation: " + operation + " when setting: " + attributeName + " in: " + this.name,
});
}
}
}
-
}
else
{
- throw Object.assign(response, {error: 'operation: ' + operation + ' is invalid for old value: ' + JSON.stringify(oldValue) + ' and new value: ' + JSON.stringify(attributeValue)});
+ throw Object.assign(response, {
+ error: "operation: " + operation + " is invalid for old value: " + JSON.stringify(oldValue) + " and new value: " + JSON.stringify(attributeValue),
+ });
}
}
-
// (*) log if appropriate:
- if (!stealth && (log || this._autoLog) && (typeof this.win !== 'undefined'))
+ if (!stealth && (log || this._autoLog) && (typeof this.win !== "undefined"))
{
const msg = this.name + ": " + attributeName + " = " + util.toString(attributeValue);
this.win.logOnFlip({
@@ -317,13 +308,12 @@ export class PsychObject extends EventEmitter
});
}
-
// (*) set the value of the attribute and return whether it has changed:
- const previousAttributeValue = this['_' + attributeName];
- this['_' + attributeName] = attributeValue;
+ const previousAttributeValue = this["_" + attributeName];
+ this["_" + attributeName] = attributeValue;
// Things seem OK without this check except for 'vertices'
- if (typeof previousAttributeValue === 'undefined')
+ if (typeof previousAttributeValue === "undefined")
{
// Not that any of the following lines should throw, but evaluating
// `this._vertices.map` on `ShapeStim._getVertices_px()` seems to
@@ -342,10 +332,9 @@ export class PsychObject extends EventEmitter
// `Util.toString()` might try, but fail to stringify in a meaningful way are assigned
// an 'Object (circular)' string representation. For being opaque as to their raw
// value, those types of input are liable to produce PIXI updates.
- return prev === 'Object (circular)' || next === 'Object (circular)' || prev !== next;
+ return prev === "Object (circular)" || next === "Object (circular)" || prev !== next;
}
-
/**
* Add an attribute to this instance (e.g. define setters and getters) and affect a value to it.
*
@@ -355,20 +344,21 @@ export class PsychObject extends EventEmitter
* @param {object} [defaultValue] - the default value for the attribute
* @param {function} [onChange] - function called upon changes to the attribute value
*/
- _addAttribute(name, value, defaultValue = undefined, onChange = () => {})
+ _addAttribute(name, value, defaultValue = undefined, onChange = () =>
+ {})
{
- const getPropertyName = 'get' + name[0].toUpperCase() + name.substr(1);
- if (typeof this[getPropertyName] === 'undefined')
+ const getPropertyName = "get" + name[0].toUpperCase() + name.substr(1);
+ if (typeof this[getPropertyName] === "undefined")
{
- this[getPropertyName] = () => this['_' + name];
+ this[getPropertyName] = () => this["_" + name];
}
- const setPropertyName = 'set' + name[0].toUpperCase() + name.substr(1);
- if (typeof this[setPropertyName] === 'undefined')
+ const setPropertyName = "set" + name[0].toUpperCase() + name.substr(1);
+ if (typeof this[setPropertyName] === "undefined")
{
this[setPropertyName] = (value, log = false) =>
{
- if (typeof value === 'undefined' || value === null)
+ if (typeof value === "undefined" || value === null)
{
value = defaultValue;
}
@@ -382,7 +372,7 @@ export class PsychObject extends EventEmitter
else
{
// deal with default value:
- if (typeof value === 'undefined' || value === null)
+ if (typeof value === "undefined" || value === null)
{
value = defaultValue;
}
@@ -397,16 +387,14 @@ export class PsychObject extends EventEmitter
set(value)
{
this[setPropertyName](value);
- }
+ },
});
-
// note: we use this[name] instead of this['_' + name] since a this.set method may available
// in the object, in which case we need to call it
this[name] = value;
- //this['_' + name] = value;
+ // this['_' + name] = value;
this._userAttributes.add(name);
}
-
}
diff --git a/src/util/Scheduler.js b/src/util/Scheduler.js
index 4198b06..1521970 100644
--- a/src/util/Scheduler.js
+++ b/src/util/Scheduler.js
@@ -7,7 +7,6 @@
* @license Distributed under the terms of the MIT License
*/
-
/**
* A scheduler helps run the main loop by managing scheduled functions,
* called tasks, after each frame is displayed.
@@ -53,7 +52,6 @@ export class Scheduler
this._status = Scheduler.Status.STOPPED;
}
-
/**
* Get the status of the scheduler.
*
@@ -66,7 +64,6 @@ export class Scheduler
return this._status;
}
-
/**
* Task to be run by the scheduler.
*
@@ -87,7 +84,6 @@ export class Scheduler
this._argsList.push(args);
}
-
/**
* Condition evaluated when the task is run.
*
@@ -108,7 +104,7 @@ export class Scheduler
addConditional(condition, thenScheduler, elseScheduler)
{
const self = this;
- let task = function ()
+ let task = function()
{
if (condition())
{
@@ -125,7 +121,6 @@ export class Scheduler
this.add(task);
}
-
/**
* Start this scheduler.
*
@@ -173,7 +168,6 @@ export class Scheduler
requestAnimationFrame(update);
}
-
/**
* Stop this scheduler.
*
@@ -187,7 +181,6 @@ export class Scheduler
this._stopAtNextUpdate = true;
}
-
/**
* Run the next scheduled tasks, in sequence, until a rendering of the scene is requested.
*
@@ -209,9 +202,8 @@ export class Scheduler
}
// if there is no current task, we look for the next one in the list or quit if there is none:
- if (typeof this._currentTask == 'undefined')
+ if (typeof this._currentTask == "undefined")
{
-
// a task is available in the taskList:
if (this._taskList.length > 0)
{
@@ -259,15 +251,12 @@ export class Scheduler
this._currentTask = undefined;
this._currentArgs = undefined;
}
-
}
return state;
}
-
}
-
/**
* Events.
*
@@ -280,25 +269,24 @@ Scheduler.Event = {
/**
* Move onto the next task *without* rendering the scene first.
*/
- NEXT: Symbol.for('NEXT'),
+ NEXT: Symbol.for("NEXT"),
/**
* Render the scene and repeat the task.
*/
- FLIP_REPEAT: Symbol.for('FLIP_REPEAT'),
+ FLIP_REPEAT: Symbol.for("FLIP_REPEAT"),
/**
* Render the scene and move onto the next task.
*/
- FLIP_NEXT: Symbol.for('FLIP_NEXT'),
+ FLIP_NEXT: Symbol.for("FLIP_NEXT"),
/**
* Quit the scheduler.
*/
- QUIT: Symbol.for('QUIT')
+ QUIT: Symbol.for("QUIT"),
};
-
/**
* Status.
*
@@ -311,10 +299,10 @@ Scheduler.Status = {
/**
* The Scheduler is running.
*/
- RUNNING: Symbol.for('RUNNING'),
+ RUNNING: Symbol.for("RUNNING"),
/**
* The Scheduler is stopped.
*/
- STOPPED: Symbol.for('STOPPED')
+ STOPPED: Symbol.for("STOPPED"),
};
diff --git a/src/util/Util.js b/src/util/Util.js
index 1d52caa..32c2299 100644
--- a/src/util/Util.js
+++ b/src/util/Util.js
@@ -7,9 +7,6 @@
* @license Distributed under the terms of the MIT License
*/
-import * as PIXI from 'pixi.js-legacy';
-
-
/**
* Syntactic sugar for Mixins
*
@@ -45,7 +42,6 @@ class MixinBuilder
}
}
-
/**
* Convert the resulting value of a promise into a tupple.
*
@@ -59,11 +55,10 @@ class MixinBuilder
export function promiseToTupple(promise)
{
return promise
- .then(data => [null, data])
- .catch(error => [error, null]);
+ .then((data) => [null, data])
+ .catch((error) => [error, null]);
}
-
/**
* Get a Universally Unique Identifier (RFC4122 version 4)
* See details here: https://www.ietf.org/rfc/rfc4122.txt
@@ -75,14 +70,13 @@ export function promiseToTupple(promise)
*/
export function makeUuid()
{
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c)
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c)
{
- const r = Math.random() * 16 | 0, v = (c === 'x') ? r : (r & 0x3 | 0x8);
+ const r = Math.random() * 16 | 0, v = (c === "x") ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
-
/**
* Get the error stack of the calling, exception-throwing function.
*
@@ -95,7 +89,7 @@ export function getErrorStack()
{
try
{
- throw Error('');
+ throw Error("");
}
catch (error)
{
@@ -103,11 +97,10 @@ export function getErrorStack()
let stack = error.stack.split("\n");
stack.splice(1, 1);
- return JSON.stringify(stack.join('\n'));
+ return JSON.stringify(stack.join("\n"));
}
}
-
/**
* Test if x is an 'empty' value.
*
@@ -119,7 +112,7 @@ export function getErrorStack()
*/
export function isEmpty(x)
{
- if (typeof x === 'undefined')
+ if (typeof x === "undefined")
{
return true;
}
@@ -131,7 +124,7 @@ export function isEmpty(x)
{
return true;
}
- if (x.length === 1 && typeof x[0] === 'undefined')
+ if (x.length === 1 && typeof x[0] === "undefined")
{
return true;
}
@@ -139,7 +132,6 @@ export function isEmpty(x)
return false;
}
-
/**
* Detect the user's browser.
*
@@ -155,71 +147,70 @@ export function isEmpty(x)
export function detectBrowser()
{
// Opera 8.0+
- const isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
+ const isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(" OPR/") >= 0;
if (isOpera)
{
- return 'Opera';
+ return "Opera";
}
// Firefox 1.0+
- const isFirefox = (typeof InstallTrigger !== 'undefined');
+ const isFirefox = (typeof InstallTrigger !== "undefined");
if (isFirefox)
{
- return 'Firefox';
+ return "Firefox";
}
- // Safari 3.0+ "[object HTMLElementConstructor]"
- const isSafari = /constructor/i.test(window.HTMLElement) || (function (p)
+ // Safari 3.0+ "[object HTMLElementConstructor]"
+ const isSafari = /constructor/i.test(window.HTMLElement) || (function(p)
{
return p.toString() === "[object SafariRemoteNotification]";
- })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
+ })(!window["safari"] || (typeof safari !== "undefined" && safari.pushNotification));
if (isSafari)
{
- return 'Safari';
+ return "Safari";
}
// Internet Explorer 6-11
// const isIE6 = !window.XMLHttpRequest;
// const isIE7 = document.all && window.XMLHttpRequest && !XDomainRequest && !window.opera;
// const isIE8 = document.documentMode==8;
- const isIE = /*@cc_on!@*/false || !!document.documentMode;
+ const isIE = /*@cc_on!@*/ false || !!document.documentMode;
if (isIE)
{
- return 'IE';
+ return "IE";
}
// Edge 20+
const isEdge = !isIE && !!window.StyleMedia;
if (isEdge)
{
- return 'Edge';
+ return "Edge";
}
// Chrome 1+
const isChrome = window.chrome;
if (isChrome)
{
- return 'Chrome';
+ return "Chrome";
}
// Chromium-based Edge:
const isEdgeChromium = isChrome && (navigator.userAgent.indexOf("Edg") !== -1);
if (isEdgeChromium)
{
- return 'EdgeChromium';
+ return "EdgeChromium";
}
// Blink engine detection
const isBlink = (isChrome || isOpera) && !!window.CSS;
if (isBlink)
{
- return 'Blink';
+ return "Blink";
}
- return 'unknown';
+ return "unknown";
}
-
/**
* Convert obj to its numerical form.
*
@@ -239,24 +230,23 @@ export function detectBrowser()
export function toNumerical(obj)
{
const response = {
- origin: 'util.toNumerical',
- context: 'when converting an object to its numerical form'
+ origin: "util.toNumerical",
+ context: "when converting an object to its numerical form",
};
try
{
-
if (obj === null)
{
- throw 'unable to convert null to a number';
+ throw "unable to convert null to a number";
}
- if (typeof obj === 'undefined')
+ if (typeof obj === "undefined")
{
- throw 'unable to convert undefined to a number';
+ throw "unable to convert undefined to a number";
}
- if (typeof obj === 'number')
+ if (typeof obj === "number")
{
return obj;
}
@@ -285,20 +275,32 @@ export function toNumerical(obj)
return arrayMaybe.map(convertToNumber);
}
- if (typeof obj === 'string')
+ if (typeof obj === "string")
{
return convertToNumber(obj);
}
- throw 'unable to convert the object to a number';
+ throw "unable to convert the object to a number";
}
catch (error)
{
throw Object.assign(response, { error });
}
-
}
+/**
+ * Check whether a value looks like a number
+ *
+ * @name module:util.isNumeric
+ * @function
+ * @public
+ * @param {*} input - Some value
+ * @return {boolean} Whether or not the value can be converted into a number
+ */
+export function isNumeric(input)
+{
+ return Number.isNaN(Number(input)) === false;
+}
/**
* Check whether a point lies within a polygon
@@ -331,7 +333,6 @@ export function IsPointInsidePolygon(point, vertices)
return isInside;
}
-
/**
* Shuffle an array in place using the Fisher-Yastes's modern algorithm
* See details here: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
@@ -345,7 +346,8 @@ export function IsPointInsidePolygon(point, vertices)
*/
export function shuffle(array, randomNumberGenerator = undefined)
{
- if (randomNumberGenerator === undefined) {
+ if (randomNumberGenerator === undefined)
+ {
randomNumberGenerator = Math.random;
}
for (let i = array.length - 1; i > 0; i--)
@@ -356,8 +358,6 @@ export function shuffle(array, randomNumberGenerator = undefined)
return array;
}
-
-
/**
* Get the position of the object, in pixel units
*
@@ -371,21 +371,21 @@ export function shuffle(array, randomNumberGenerator = undefined)
export function getPositionFromObject(object, units)
{
const response = {
- origin: 'util.getPositionFromObject',
- context: 'when getting the position of an object'
+ origin: "util.getPositionFromObject",
+ context: "when getting the position of an object",
};
try
{
- if (typeof object === 'undefined')
+ if (typeof object === "undefined")
{
- throw 'cannot get the position of an undefined object';
+ throw "cannot get the position of an undefined object";
}
let objectWin = undefined;
// the object has a getPos function:
- if (typeof object.getPos === 'function')
+ if (typeof object.getPos === "function")
{
units = object.units;
objectWin = object.win;
@@ -397,12 +397,10 @@ export function getPositionFromObject(object, units)
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Convert the position to pixel units.
*
@@ -418,28 +416,28 @@ export function getPositionFromObject(object, units)
export function to_px(pos, posUnit, win, integerCoordinates = false)
{
const response = {
- origin: 'util.to_px',
- context: 'when converting a position to pixel units'
+ origin: "util.to_px",
+ context: "when converting a position to pixel units",
};
let pos_px;
- if (posUnit === 'pix')
+ if (posUnit === "pix")
{
pos_px = pos;
}
- else if (posUnit === 'norm')
+ else if (posUnit === "norm")
{
pos_px = [pos[0] * win.size[0] / 2.0, pos[1] * win.size[1] / 2.0];
}
- else if (posUnit === 'height')
+ else if (posUnit === "height")
{
const minSize = Math.min(win.size[0], win.size[1]);
pos_px = [pos[0] * minSize, pos[1] * minSize];
}
else
{
- throw Object.assign(response, {error: `unknown position units: ${posUnit}`});
+ throw Object.assign(response, { error: `unknown position units: ${posUnit}` });
}
if (integerCoordinates)
@@ -452,7 +450,6 @@ export function to_px(pos, posUnit, win, integerCoordinates = false)
}
}
-
/**
* Convert the position to norm units.
*
@@ -466,26 +463,25 @@ export function to_px(pos, posUnit, win, integerCoordinates = false)
*/
export function to_norm(pos, posUnit, win)
{
- const response = {origin: 'util.to_norm', context: 'when converting a position to norm units'};
+ const response = { origin: "util.to_norm", context: "when converting a position to norm units" };
- if (posUnit === 'norm')
+ if (posUnit === "norm")
{
return pos;
}
- if (posUnit === 'pix')
+ if (posUnit === "pix")
{
return [pos[0] / (win.size[0] / 2.0), pos[1] / (win.size[1] / 2.0)];
}
- if (posUnit === 'height')
+ if (posUnit === "height")
{
const minSize = Math.min(win.size[0], win.size[1]);
return [pos[0] * minSize / (win.size[0] / 2.0), pos[1] * minSize / (win.size[1] / 2.0)];
}
- throw Object.assign(response, {error: `unknown position units: ${posUnit}`});
+ throw Object.assign(response, { error: `unknown position units: ${posUnit}` });
}
-
/**
* Convert the position to height units.
*
@@ -500,29 +496,28 @@ export function to_norm(pos, posUnit, win)
export function to_height(pos, posUnit, win)
{
const response = {
- origin: 'util.to_height',
- context: 'when converting a position to height units'
+ origin: "util.to_height",
+ context: "when converting a position to height units",
};
- if (posUnit === 'height')
+ if (posUnit === "height")
{
return pos;
}
- if (posUnit === 'pix')
+ if (posUnit === "pix")
{
const minSize = Math.min(win.size[0], win.size[1]);
return [pos[0] / minSize, pos[1] / minSize];
}
- if (posUnit === 'norm')
+ if (posUnit === "norm")
{
const minSize = Math.min(win.size[0], win.size[1]);
return [pos[0] * win.size[0] / 2.0 / minSize, pos[1] * win.size[1] / 2.0 / minSize];
}
- throw Object.assign(response, {error: `unknown position units: ${posUnit}`});
+ throw Object.assign(response, { error: `unknown position units: ${posUnit}` });
}
-
/**
* Convert the position to window units.
*
@@ -536,19 +531,19 @@ export function to_height(pos, posUnit, win)
*/
export function to_win(pos, posUnit, win)
{
- const response = {origin: 'util.to_win', context: 'when converting a position to window units'};
+ const response = { origin: "util.to_win", context: "when converting a position to window units" };
try
{
- if (win._units === 'pix')
+ if (win._units === "pix")
{
return to_px(pos, posUnit, win);
}
- if (win._units === 'norm')
+ if (win._units === "norm")
{
return to_norm(pos, posUnit, win);
}
- if (win._units === 'height')
+ if (win._units === "height")
{
return to_height(pos, posUnit, win);
}
@@ -557,11 +552,10 @@ export function to_win(pos, posUnit, win)
}
catch (error)
{
- throw Object.assign(response, {response, error});
+ throw Object.assign(response, { response, error });
}
}
-
/**
* Convert the position to given units.
*
@@ -576,19 +570,19 @@ export function to_win(pos, posUnit, win)
*/
export function to_unit(pos, posUnit, win, targetUnit)
{
- const response = {origin: 'util.to_unit', context: 'when converting a position to different units'};
+ const response = { origin: "util.to_unit", context: "when converting a position to different units" };
try
{
- if (targetUnit === 'pix')
+ if (targetUnit === "pix")
{
return to_px(pos, posUnit, win);
}
- if (targetUnit === 'norm')
+ if (targetUnit === "norm")
{
return to_norm(pos, posUnit, win);
}
- if (targetUnit === 'height')
+ if (targetUnit === "height")
{
return to_height(pos, posUnit, win);
}
@@ -597,37 +591,10 @@ export function to_unit(pos, posUnit, win, targetUnit)
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-/**
- * Convert a position to a PIXI Point.
- *
- * @name module:util.to_pixiPoint
- * @function
- * @public
- * @param {number[]} pos - the input position
- * @param {string} posUnit - the position units
- * @param {Window} win - the associated Window
- * @param {boolean} [integerCoordinates = false] - whether or not to round the PIXI Point coordinates.
- * @returns {number[]} the position as a PIXI Point
- */
-export function to_pixiPoint(pos, posUnit, win, integerCoordinates = false)
-{
- const pos_px = to_px(pos, posUnit, win);
- if (integerCoordinates)
- {
- return new PIXI.Point(Math.round(pos_px[0]), Math.round(pos_px[1]));
- }
- else
- {
- return new PIXI.Point(pos_px[0], pos_px[1]);
- }
-}
-
-
/**
* Convert an object to its string representation, taking care of symbols.
*
@@ -641,23 +608,23 @@ export function to_pixiPoint(pos, posUnit, win, integerCoordinates = false)
*/
export function toString(object)
{
- if (typeof object === 'undefined')
+ if (typeof object === "undefined")
{
- return 'undefined';
+ return "undefined";
}
if (!object)
{
- return 'null';
+ return "null";
}
- if (typeof object === 'string')
+ if (typeof object === "string")
{
return object;
}
// if the object is a class and has a toString method:
- if (object.constructor.toString().substring(0, 5) === 'class' && typeof object.toString === 'function')
+ if (object.constructor.toString().substring(0, 5) === "class" && typeof object.toString === "function")
{
return object.toString();
}
@@ -666,7 +633,7 @@ export function toString(object)
{
const symbolReplacer = (key, value) =>
{
- if (typeof value === 'symbol')
+ if (typeof value === "symbol")
{
value = Symbol.keyFor(value);
}
@@ -676,30 +643,28 @@ export function toString(object)
}
catch (e)
{
- return 'Object (circular)';
+ return "Object (circular)";
}
}
-
if (!String.prototype.format)
{
- String.prototype.format = function ()
+ String.prototype.format = function()
{
var args = arguments;
return this
- .replace(/{(\d+)}/g, function (match, number)
+ .replace(/{(\d+)}/g, function(match, number)
{
- return typeof args[number] != 'undefined' ? args[number] : match;
+ return typeof args[number] != "undefined" ? args[number] : match;
})
- .replace(/{([$_a-zA-Z][$_a-zA-Z0-9]*)}/g, function (match, name)
+ .replace(/{([$_a-zA-Z][$_a-zA-Z0-9]*)}/g, function(match, name)
{
- //console.log("n=" + name + " args[0][name]=" + args[0][name]);
+ // console.log("n=" + name + " args[0][name]=" + args[0][name]);
return args.length > 0 && args[0][name] !== undefined ? args[0][name] : match;
});
};
}
-
/**
* Get the most informative error from the server response from a jquery server request.
*
@@ -712,17 +677,17 @@ if (!String.prototype.format)
*/
export function getRequestError(jqXHR, textStatus, errorThrown)
{
- let errorMsg = 'unknown error';
+ let errorMsg = "unknown error";
- if (typeof jqXHR.responseJSON !== 'undefined')
+ if (typeof jqXHR.responseJSON !== "undefined")
{
errorMsg = jqXHR.responseJSON;
}
- else if (typeof jqXHR.responseText !== 'undefined')
+ else if (typeof jqXHR.responseText !== "undefined")
{
errorMsg = jqXHR.responseText;
}
- else if (typeof errorThrown !== 'undefined')
+ else if (typeof errorThrown !== "undefined")
{
errorMsg = errorThrown;
}
@@ -730,7 +695,6 @@ export function getRequestError(jqXHR, textStatus, errorThrown)
return errorMsg;
}
-
/**
* Test whether an object is either an integer or the string representation of an integer.
* This is adapted from: https://stackoverflow.com/a/14794066
@@ -752,7 +716,6 @@ export function isInt(obj)
return (x | 0) === x;
}
-
/**
* Get the URL parameters.
*
@@ -779,7 +742,6 @@ export function getUrlParameters()
return urlMap;*/
}
-
/**
* Add info extracted from the URL to the given dictionary.
*
@@ -800,7 +762,7 @@ export function addInfoFromUrl(info)
// for (const [key, value] of infoFromUrl)
infoFromUrl.forEach((value, key) =>
{
- if (key.indexOf('__') !== 0)
+ if (key.indexOf("__") !== 0)
{
info[key] = value;
}
@@ -809,7 +771,6 @@ export function addInfoFromUrl(info)
return info;
}
-
/**
* Select values from an array.
*
@@ -832,28 +793,30 @@ export function addInfoFromUrl(info)
*/
export function selectFromArray(array, selection)
{
-
// if selection is an integer, or a string representing an integer, we treat it as an index in the array
// and return that entry:
if (isInt(selection))
{
return [array[parseInt(selection)]];
- }// if selection is an array, we treat it as a list of indices
+ }
+ // if selection is an array, we treat it as a list of indices
// and return an array with the entries corresponding to those indices:
else if (Array.isArray(selection))
{
// Pick out `array` items matching indices contained in `selection` in order
- return selection.map(i => array[i]);
- }// if selection is a string, we decode it:
- else if (typeof selection === 'string')
+ return selection.map((i) => array[i]);
+ }
+ // if selection is a string, we decode it:
+ else if (typeof selection === "string")
{
- if (selection.indexOf(',') > -1)
+ if (selection.indexOf(",") > -1)
{
- return selection.split(',').map(a => selectFromArray(array, a));
- }// return flattenArray( selection.split(',').map(a => selectFromArray(array, a)) );
- else if (selection.indexOf(':') > -1)
+ return selection.split(",").map((a) => selectFromArray(array, a));
+ }
+ // return flattenArray( selection.split(',').map(a => selectFromArray(array, a)) );
+ else if (selection.indexOf(":") > -1)
{
- let sliceParams = selection.split(':').map(a => parseInt(a));
+ let sliceParams = selection.split(":").map((a) => parseInt(a));
if (sliceParams.length === 3)
{
return sliceArray(array, sliceParams[0], sliceParams[2], sliceParams[1]);
@@ -864,18 +827,16 @@ export function selectFromArray(array, selection)
}
}
}
-
else
{
throw {
- origin: 'selectFromArray',
- context: 'when selecting entries from an array',
- error: 'unknown selection type: ' + (typeof selection)
+ origin: "selectFromArray",
+ context: "when selecting entries from an array",
+ error: "unknown selection type: " + (typeof selection),
};
}
}
-
/**
* Recursively flatten an array of arrays.
*
@@ -893,11 +854,10 @@ export function flattenArray(array)
flat.push((Array.isArray(next) && Array.isArray(next[0])) ? flattenArray(next) : next);
return flat;
},
- []
+ [],
);
}
-
/**
* Slice an array.
*
@@ -944,7 +904,6 @@ export function sliceArray(array, from = NaN, to = NaN, step = NaN)
}
}
-
/**
* Offer data as download in the browser.
*
@@ -957,14 +916,14 @@ export function sliceArray(array, from = NaN, to = NaN, step = NaN)
*/
export function offerDataForDownload(filename, data, type)
{
- const blob = new Blob([data], {type});
+ const blob = new Blob([data], { type });
if (window.navigator.msSaveOrOpenBlob)
{
window.navigator.msSaveBlob(blob, filename);
}
else
{
- const anchor = document.createElement('a');
+ const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(blob);
anchor.download = filename;
document.body.appendChild(anchor);
@@ -973,7 +932,6 @@ export function offerDataForDownload(filename, data, type)
}
}
-
/**
* Convert a string representing a JSON array, e.g. "[1, 2]" into an array, e.g. ["1","2"].
* This approach overcomes the built-in JSON parsing limitations when it comes to eg. floats
@@ -1007,14 +965,13 @@ export function turnSquareBracketsIntoArrays(input, max = 1)
// Reformat content for each match
const matches = matchesMaybe.map((data) =>
- {
- return data
- // Remove the square brackets
- .replace(/[\[\]]+/g, '')
- // Eat up space after comma
- .split(/[, ]+/);
- }
- );
+ {
+ return data
+ // Remove the square brackets
+ .replace(/[\[\]]+/g, "")
+ // Eat up space after comma
+ .split(/[, ]+/);
+ });
if (max < 2)
{
@@ -1024,7 +981,6 @@ export function turnSquareBracketsIntoArrays(input, max = 1)
return matches;
}
-
/**
* Generates random integers a-la NumPy's in the "half-open" interval [min, max). In other words, from min inclusive to max exclusive. When max is undefined, as is the case by default, results are chosen from [0, min). An error is thrown if max is less than min.
*
@@ -1040,7 +996,7 @@ export function randint(min = 0, max)
let lo = min;
let hi = max;
- if (typeof max === 'undefined')
+ if (typeof max === "undefined")
{
hi = lo;
lo = 0;
@@ -1049,16 +1005,15 @@ export function randint(min = 0, max)
if (hi < lo)
{
throw {
- origin: 'util.randint',
- context: 'when generating a random integer',
- error: 'min should be <= max'
+ origin: "util.randint",
+ context: "when generating a random integer",
+ error: "min should be <= max",
};
}
return Math.floor(Math.random() * (hi - lo)) + lo;
}
-
/**
* Round to a certain number of decimal places.
*
@@ -1077,7 +1032,6 @@ export function round(input, places = 0)
return +(Math.round(`${input}e+${places}`) + `e-${places}`);
}
-
/**
* Calculate the sum of the elements in the input array.
*
@@ -1101,14 +1055,13 @@ export function sum(input = [], start = 0)
return input
// type cast everything as a number
- .map(value => Number(value))
+ .map((value) => Number(value))
// drop non numeric looking entries (note: needs transpiling for IE11)
- .filter(value => Number.isNaN(value) === false)
+ .filter((value) => Number.isNaN(value) === false)
// add up each successive entry, starting with start
.reduce(add, start);
}
-
/**
* Calculate the average of the elements in the input array.
*
@@ -1135,10 +1088,9 @@ export function average(input = [])
return sum(input, 0) / input.length;
}
-
/**
* Sort the elements of the input array, in increasing alphabetical or numerical order.
- *
+ *
* @name module:util.sort
* @function
* @public
@@ -1150,44 +1102,43 @@ export function average(input = [])
export function sort(input)
{
const response = {
- origin: 'util.sort',
- context: 'when sorting the elements of an array'
+ origin: "util.sort",
+ context: "when sorting the elements of an array",
};
try
{
if (!Array.isArray(input))
{
- throw 'the input argument should be an array';
+ throw "the input argument should be an array";
}
// check the type and consistency of the array, and sort it accordingly:
- const isNumberArray = input.every(element => typeof element === "number");
+ const isNumberArray = input.every((element) => typeof element === "number");
if (isNumberArray)
{
return input.sort((a, b) => (a - b));
}
- const isStringArray = input.every(element => typeof element === "string");
+ const isStringArray = input.every((element) => typeof element === "string");
if (isStringArray)
{
return input.sort();
}
-
- throw 'the input array should either consist entirely of strings or of numbers';
+
+ throw "the input array should either consist entirely of strings or of numbers";
}
catch (error)
{
- throw {...response, error};
- }
- }
-
-
+ throw { ...response, error };
+ }
+}
+
/**
* Create a sequence of integers.
- *
+ *
* The sequence is such that the integer at index i is: start + step * i, with i >= 0 and start + step * i < stop
- *
+ *
* Note: this is a JavaScript implement of the Python range function, which explains the unusual management of arguments.
*
* @name module:util.range
@@ -1201,8 +1152,8 @@ export function sort(input)
export function range(...args)
{
const response = {
- origin: 'util.range',
- context: 'when building a range of numbers'
+ origin: "util.range",
+ context: "when building a range of numbers",
};
try
@@ -1212,9 +1163,10 @@ export function range(...args)
switch (args.length)
{
case 0:
- throw 'at least one argument is required';
+ throw "at least one argument is required";
// 1 arg: start = 0, stop = arg, step = 1
+
case 1:
start = 0;
stop = args[0];
@@ -1222,6 +1174,7 @@ export function range(...args)
break;
// 2 args: start = arg1, stop = arg2
+
case 2:
start = args[0];
stop = args[1];
@@ -1229,6 +1182,7 @@ export function range(...args)
break;
// 3 args:
+
case 3:
start = args[0];
stop = args[1];
@@ -1236,17 +1190,20 @@ export function range(...args)
break;
default:
- throw 'range requires at least one and at most 3 arguments'
+ throw "range requires at least one and at most 3 arguments";
}
- if (!Number.isInteger(start)) {
- throw 'start should be an integer';
+ if (!Number.isInteger(start))
+ {
+ throw "start should be an integer";
}
- if (!Number.isInteger(stop)) {
- throw 'stop should be an integer';
+ if (!Number.isInteger(stop))
+ {
+ throw "stop should be an integer";
}
- if (!Number.isInteger(step)) {
- throw 'step should be an integer';
+ if (!Number.isInteger(step))
+ {
+ throw "step should be an integer";
}
// if start >= stop, the range is empty:
@@ -1264,14 +1221,13 @@ export function range(...args)
}
catch (error)
{
- throw {...response, error};
+ throw { ...response, error };
}
}
-
/**
* Create a boolean function that compares an input element to the given value.
- *
+ *
* @name module:util._match
* @function
* @private
@@ -1281,16 +1237,16 @@ export function range(...args)
function _match(value)
{
const response = {
- origin: 'util._match',
- context: 'when creating a function that compares an input element to the given value'
+ origin: "util._match",
+ context: "when creating a function that compares an input element to the given value",
};
try
{
// function:
- if (typeof value === 'function')
+ if (typeof value === "function")
{
- throw 'the value cannot be a function';
+ throw "the value cannot be a function";
}
// NaN:
@@ -1306,19 +1262,19 @@ function _match(value)
}
// object: we compare using JSON.stringify
- if (typeof value === 'object')
+ if (typeof value === "object")
{
const jsonValue = JSON.stringify(value);
- if (typeof jsonValue === 'undefined')
+ if (typeof jsonValue === "undefined")
{
- throw 'value could not be converted to a JSON string';
+ throw "value could not be converted to a JSON string";
}
return (element) =>
{
const jsonElement = JSON.stringify(element);
return (jsonElement === jsonValue);
- }
+ };
}
// everything else:
@@ -1326,16 +1282,15 @@ function _match(value)
}
catch (error)
{
- throw {...response, error};
- }
- }
-
+ throw { ...response, error };
+ }
+}
- /**
+/**
* Count the number of elements in the input array that match the given value.
- *
+ *
* Note: count is able to handle NaN, null, as well as any value convertible to a JSON string.
- *
+ *
* @name module:util.count
* @function
* @public
@@ -1343,44 +1298,43 @@ function _match(value)
* @param {Number|string|object|null} value the matching value
* @returns the number of matching elements
*/
- export function count(input, value)
- {
+export function count(input, value)
+{
const response = {
- origin: 'util.count',
- context: 'when counting how many elements in the input array match the given value'
+ origin: "util.count",
+ context: "when counting how many elements in the input array match the given value",
};
try
{
if (!Array.isArray(input))
{
- throw 'the input argument should be an array';
+ throw "the input argument should be an array";
}
const match = _match(value);
let nbMatches = 0;
- input.forEach(element =>
+ input.forEach((element) =>
+ {
+ if (match(element))
{
- if (match(element))
- {
- ++ nbMatches;
- }
- });
+ ++nbMatches;
+ }
+ });
return nbMatches;
}
catch (error)
{
- throw {...response, error};
+ throw { ...response, error };
}
- }
-
+}
- /**
+/**
* Get the index in the input array of the first element that matches the given value.
- *
+ *
* Note: index is able to handle NaN, null, as well as any value convertible to a JSON string.
- *
+ *
* @name module:util.index
* @function
* @public
@@ -1389,18 +1343,18 @@ function _match(value)
* @returns the index of the first element that matches the value
* @throws if the input array does not contain any matching element
*/
- export function index(input, value)
- {
+export function index(input, value)
+{
const response = {
- origin: 'util.index',
- context: 'when getting the index in the input array of the first element that matches the given value'
+ origin: "util.index",
+ context: "when getting the index in the input array of the first element that matches the given value",
};
try
{
if (!Array.isArray(input))
{
- throw 'the input argument should be an array';
+ throw "the input argument should be an array";
}
const match = _match(value);
@@ -1408,18 +1362,16 @@ function _match(value)
if (index === -1)
{
- throw 'no element in the input array matches the value';
+ throw "no element in the input array matches the value";
}
return index;
-
}
catch (error)
{
- throw {...response, error};
+ throw { ...response, error };
}
- }
-
+}
/**
* Return the file extension corresponding to an audio mime type.
@@ -1434,29 +1386,29 @@ function _match(value)
*/
export function extensionFromMimeType(mimeType)
{
- if (typeof mimeType !== 'string')
+ if (typeof mimeType !== "string")
{
- return '.dat';
+ return ".dat";
}
- if (mimeType.indexOf('audio/webm') === 0)
+ if (mimeType.indexOf("audio/webm") === 0)
{
- return '.webm';
+ return ".webm";
}
- if (mimeType.indexOf('audio/ogg') === 0)
+ if (mimeType.indexOf("audio/ogg") === 0)
{
- return '.ogg';
+ return ".ogg";
}
- if (mimeType.indexOf('audio/wav') === 0)
+ if (mimeType.indexOf("audio/wav") === 0)
{
- return '.wav';
+ return ".wav";
}
- if (mimeType.indexOf('video/webm') === 0)
+ if (mimeType.indexOf("video/webm") === 0)
{
- return '.webm';
+ return ".webm";
}
return '.dat';
diff --git a/src/util/Util.test.js b/src/util/Util.test.js
new file mode 100644
index 0000000..9ffaa25
--- /dev/null
+++ b/src/util/Util.test.js
@@ -0,0 +1,113 @@
+import assert from "assert";
+import { isNumeric, randint, round, sum, toNumerical, turnSquareBracketsIntoArrays } from "./Util.js";
+
+assert(isNumeric("1.2"));
+assert(isNumeric(0));
+assert(!isNumeric("NaN"));
+assert(!isNumeric("hey"));
+
+// number -> number, e.g. 2 -> 2
+assert.equal(2, toNumerical(2));
+
+// [number] -> [number], e.g. [1,2,3] -> [1,2,3]
+assert.deepEqual([1, 2, 3], toNumerical([1, 2, 3]));
+assert(Array.isArray(toNumerical([0])));
+
+// numeral string -> number, e.g. "8" -> 8
+assert.deepEqual(8, toNumerical("8"));
+
+// [number | numeral string] -> [number], e.g. [1, 2, "3"] -> [1,2,3]
+assert.deepEqual([1, 2, 3], toNumerical([1, 2, "3"]));
+
+// Establish what happens when fed an array-like string
+assert.deepEqual([1, 2, 3], toNumerical(...turnSquareBracketsIntoArrays("[1, 2, 3][]]", 2)));
+
+// Throws
+(async () =>
+{
+ await assert.rejects(
+ async () =>
+ {
+ toNumerical(turnSquareBracketsIntoArrays([1, 2]));
+ },
+ {
+ origin: "util.toNumerical",
+ context: "when converting an object to its numerical form",
+ error: "unable to convert undefined to a number",
+ },
+ );
+})();
+
+// Towards a NumPy inspired bound random integer producer
+for (let i = 0; i < 100; i += 1)
+{
+ // Calling sans arguments gives back zero no matter what
+ assert.equal(randint(), 0);
+}
+
+for (let i = 0; i < 100; i += 1)
+{
+ // Same when calling with a min of one sans max
+ assert.equal(randint(1), 0);
+}
+
+// Expect min to be zero, max to be one, result to be zero
+assert(randint(1) >= 0 === randint(1) < 1);
+
+// Same when calling with a min of one sans max
+assert.equal(randint(1), 0);
+
+for (let i = 0; i < 100; i += 1)
+{
+ // Same with null
+ assert.equal(randint(null), 0);
+}
+
+for (let i = 100; i > 0; i -= 1)
+{
+ // Try out a few ranges in the positive
+ assert(randint(i) < i);
+}
+
+for (let i = -99; i < 0; i += 1)
+{
+ // What happens when using negative parameters?
+ assert(randint(2 * i, i) <= i);
+}
+
+try
+{
+ randint(0, -10);
+}
+catch ({ error })
+{
+ assert.equal(error, "min should be <= max");
+}
+
+// Implement Crib Sheet math extras
+// These are taken from the SO question above
+// https://stackoverflow.com/questions/11832914
+const actual = [
+ 10,
+ 1.7777777,
+ 9.1,
+];
+
+const expected = [
+ 10,
+ 1.78,
+ 9.1,
+];
+
+const got = actual.map((input) => round(input, 2));
+
+assert.deepEqual(expected, got);
+
+assert.equal(sum(null), 0);
+assert.equal(sum(), 0);
+assert(!sum([0]));
+assert.equal(sum([1, NaN, null, undefined]), 1);
+assert.equal(sum([1, 2, -3]), 0);
+
+// Careful Thomas!
+assert.equal(sum(["a1", 2]), 2);
diff --git a/src/util/index.js b/src/util/index.js
index 031be3d..f100215 100644
--- a/src/util/index.js
+++ b/src/util/index.js
@@ -1,7 +1,8 @@
-export * from './Clock.js';
-export * from './Color.js';
-export * from './ColorMixin.js';
-export * from './EventEmitter.js';
-export * from './PsychObject.js';
-export * from './Scheduler.js';
-export * from './Util.js';
+export * from "./Clock.js";
+export * from "./Color.js";
+export * from "./ColorMixin.js";
+export * from "./EventEmitter.js";
+export * from "./Pixi.js";
+export * from "./PsychObject.js";
+export * from "./Scheduler.js";
+export * from "./Util.js";
diff --git a/src/visual/ButtonStim.js b/src/visual/ButtonStim.js
index 4055de4..bc422e5 100644
--- a/src/visual/ButtonStim.js
+++ b/src/visual/ButtonStim.js
@@ -7,10 +7,8 @@
* @license Distributed under the terms of the MIT License
*/
-
-import {TextBox} from './TextBox.js';
-import {Mouse} from '../core/Mouse.js';
-
+import { Mouse } from "../core/Mouse.js";
+import { TextBox } from "./TextBox.js";
/**
* ButtonStim visual stimulus.
@@ -39,28 +37,71 @@ import {Mouse} from '../core/Mouse.js';
*/
export class ButtonStim extends TextBox
{
- constructor({win, name, text, font, pos, size, padding, anchor = 'center', units, color, fillColor = 'darkgrey', borderColor, borderWidth = 0, opacity, letterHeight, bold = true, italic, autoDraw, autoLog} = {})
+ constructor(
+ {
+ win,
+ name,
+ text,
+ font,
+ pos,
+ size,
+ padding,
+ anchor = "center",
+ units,
+ color,
+ fillColor = "darkgrey",
+ borderColor,
+ borderWidth = 0,
+ opacity,
+ letterHeight,
+ bold = true,
+ italic,
+ autoDraw,
+ autoLog,
+ } = {},
+ )
{
- super({win, name, text, font, pos, size, padding, anchor, units, color, fillColor, borderColor, borderWidth, opacity, letterHeight, bold, italic, alignment: 'center', autoDraw, autoLog});
+ super({
+ win,
+ name,
+ text,
+ font,
+ pos,
+ size,
+ padding,
+ anchor,
+ units,
+ color,
+ fillColor,
+ borderColor,
+ borderWidth,
+ opacity,
+ letterHeight,
+ bold,
+ italic,
+ alignment: "center",
+ autoDraw,
+ autoLog,
+ });
- this.psychoJS.logger.debug('create a new Button with name: ', name);
+ this.psychoJS.logger.debug("create a new Button with name: ", name);
- this.listener = new Mouse({name, win, autoLog});
+ this.listener = new Mouse({ name, win, autoLog });
this._addAttribute(
- 'wasClicked',
- false
+ "wasClicked",
+ false,
);
// Arrays to store times of clicks on and off
this._addAttribute(
- 'timesOn',
- []
+ "timesOn",
+ [],
);
this._addAttribute(
- 'timesOff',
- []
+ "timesOff",
+ [],
);
if (this._autoLog)
@@ -69,8 +110,6 @@ export class ButtonStim extends TextBox
}
}
-
-
/**
* How many times has this button been clicked on?
*
@@ -82,8 +121,6 @@ export class ButtonStim extends TextBox
return this.timesOn.length;
}
-
-
/**
* Is this button currently being clicked on?
*
@@ -94,5 +131,4 @@ export class ButtonStim extends TextBox
{
return this.listener.isPressedIn(this, [1, 0, 0]);
}
-
}
diff --git a/src/visual/Camera.js b/src/visual/Camera.js
index 9c414fe..4276eba 100644
--- a/src/visual/Camera.js
+++ b/src/visual/Camera.js
@@ -7,11 +7,11 @@
* @license Distributed under the terms of the MIT License
*/
-import {Clock} from "../util/Clock";
-import {PsychObject} from "../util/PsychObject";
-import {PsychoJS} from "../core/PsychoJS";
-import * as util from '../util/Util';
-import {ExperimentHandler} from "../data/ExperimentHandler";
+import {Clock} from "../util/Clock.js";
+import {PsychObject} from "../util/PsychObject.js";
+import {PsychoJS} from "../core/PsychoJS.js";
+import * as util from "../util/Util.js";
+import {ExperimentHandler} from "../data/ExperimentHandler.js";
// import {VideoClip} from "./VideoClip";
@@ -25,20 +25,25 @@ import {ExperimentHandler} from "../data/ExperimentHandler";
* @param {string} [options.format='video/webm;codecs=vp9'] the video format
* @param {Clock} [options.clock= undefined] - an optional clock
* @param {boolean} [options.autoLog= false] - whether or not to log
+ *
+ * @todo add video constraints as parameter
*/
export class Camera extends PsychObject
{
-
+ /**
+ * @constructor
+ * @public
+ */
constructor({win, name, format, clock, autoLog} = {})
{
super(win._psychoJS);
- this._addAttribute('win', win, undefined);
- this._addAttribute('name', name, 'camera');
- this._addAttribute('format', format, 'video/webm;codecs=vp9', this._onChange);
- this._addAttribute('clock', clock, new Clock());
- this._addAttribute('autoLog', autoLog, false);
- this._addAttribute('status', PsychoJS.Status.NOT_STARTED);
+ this._addAttribute("win", win, undefined);
+ this._addAttribute("name", name, "camera");
+ this._addAttribute("format", format, "video/webm;codecs=vp9", this._onChange);
+ this._addAttribute("clock", clock, new Clock());
+ this._addAttribute("autoLog", autoLog, false);
+ this._addAttribute("status", PsychoJS.Status.NOT_STARTED);
// prepare the recording:
this._prepareRecording();
@@ -54,6 +59,7 @@ export class Camera extends PsychObject
* Get the underlying video stream.
*
* @name module:visual.Camera#getStream
+ * @function
* @public
* @returns {MediaStream} the video stream
*/
@@ -67,6 +73,7 @@ export class Camera extends PsychObject
* Get a video element pointing to the Camera stream.
*
* @name module:visual.Camera#getVideo
+ * @function
* @public
* @returns {HTMLVideoElement} a video element
*/
@@ -76,7 +83,7 @@ export class Camera extends PsychObject
// several stimuli and one of them might pause the feed
// create a video with the appropriate size:
- const video = document.createElement('video');
+ const video = document.createElement("video");
this._videos.push(video);
video.width = this._streamSettings.width;
@@ -101,6 +108,7 @@ export class Camera extends PsychObject
* Submit a request to start the recording.
*
* @name module:visual.Camera#start
+ * @function
* @public
* @return {Promise} promise fulfilled when the recording actually started
*/
@@ -116,13 +124,13 @@ export class Camera extends PsychObject
if (this._status !== PsychoJS.Status.STARTED)
{
- this._psychoJS.logger.debug('request to start video recording');
+ this._psychoJS.logger.debug("request to start video recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record video';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record video";
}
this._recorder.start();
@@ -138,12 +146,12 @@ export class Camera extends PsychObject
}
catch (error)
{
- this._psychoJS.logger.error('unable to start the video recording: ' + JSON.stringify(error));
+ this._psychoJS.logger.error("unable to start the video recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Camera.start',
- context: 'when starting the video recording for camera: ' + this._name,
+ origin: "Camera.start",
+ context: "when starting the video recording for camera: " + this._name,
error
};
}
@@ -157,6 +165,7 @@ export class Camera extends PsychObject
* Submit a request to stop the recording.
*
* @name module:visual.Camera#stop
+ * @function
* @public
* @param {Object} options
* @param {string} [options.filename] the name of the file to which the video recording
@@ -168,7 +177,7 @@ export class Camera extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to stop video recording');
+ this._psychoJS.logger.debug("request to stop video recording");
// stop the videos:
for (const video of this._videos)
@@ -201,6 +210,7 @@ export class Camera extends PsychObject
* Submit a request to pause the recording.
*
* @name module:visual.Camera#pause
+ * @function
* @public
* @return {Promise} promise fulfilled when the recording actually paused
*/
@@ -208,13 +218,13 @@ export class Camera extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED)
{
- this._psychoJS.logger.debug('request to pause video recording');
+ this._psychoJS.logger.debug("request to pause video recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record video';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record video";
}
// note: calling the pause method of the MediaRecorder raises a pause event
@@ -230,12 +240,12 @@ export class Camera extends PsychObject
}
catch (error)
{
- self._psychoJS.logger.error('unable to pause the video recording: ' + JSON.stringify(error));
+ self._psychoJS.logger.error("unable to pause the video recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Camera.pause',
- context: 'when pausing the video recording for camera: ' + this._name,
+ origin: "Camera.pause",
+ context: "when pausing the video recording for camera: " + this._name,
error
};
}
@@ -250,6 +260,7 @@ export class Camera extends PsychObject
* resume has no effect if the recording was not previously paused.
*
* @name module:visual.Camera#resume
+ * @function
* @param {Object} options
* @param {boolean} [options.clear= false] whether or not to empty the video buffer before
* resuming the recording
@@ -259,13 +270,13 @@ export class Camera extends PsychObject
{
if (this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to resume video recording');
+ this._psychoJS.logger.debug("request to resume video recording");
try
{
if (!this._recorder)
{
- throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record video';
+ throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record video";
}
// empty the audio buffer is needed:
@@ -287,12 +298,12 @@ export class Camera extends PsychObject
}
catch (error)
{
- self._psychoJS.logger.error('unable to resume the video recording: ' + JSON.stringify(error));
+ self._psychoJS.logger.error("unable to resume the video recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
- origin: 'Camera.resume',
- context: 'when resuming the video recording for camera: ' + this._name,
+ origin: "Camera.resume",
+ context: "when resuming the video recording for camera: " + this._name,
error
};
}
@@ -305,6 +316,7 @@ export class Camera extends PsychObject
* Submit a request to flush the recording.
*
* @name module:visual.Camera#flush
+ * @function
* @public
* @return {Promise} promise fulfilled when the data has actually been made available
*/
@@ -312,7 +324,7 @@ export class Camera extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
- this._psychoJS.logger.debug('request to flush video recording');
+ this._psychoJS.logger.debug("request to flush video recording");
// note: calling the requestData method of the MediaRecorder will raise a
// dataavailable event
@@ -336,13 +348,13 @@ export class Camera extends PsychObject
* @name module:visual.Camera#download
* @function
* @public
- * @param {string} filename the filename
+ * @param {string} filename - the filename of the video file
*/
- download(filename = 'video.webm')
+ download(filename = "video.webm")
{
const videoBlob = new Blob(this._videoBuffer);
- const anchor = document.createElement('a');
+ const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(videoBlob);
anchor.download = filename;
document.body.appendChild(anchor);
@@ -362,7 +374,7 @@ export class Camera extends PsychObject
async upload({tag} = {})
{
// default tag: the name of this Camera object
- if (typeof tag === 'undefined')
+ if (typeof tag === "undefined")
{
tag = this._name;
}
@@ -374,16 +386,15 @@ export class Camera extends PsychObject
// if the video recording cannot be uploaded, e.g. the experiment is running locally, or
// if it is piloting mode, then we offer the video recording as a file for download:
if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER ||
- this._psychoJS.config.experiment.status !== 'RUNNING' ||
- this._psychoJS._serverMsg.has('__pilotToken'))
+ this._psychoJS.config.experiment.status !== "RUNNING" ||
+ this._psychoJS._serverMsg.has("__pilotToken"))
{
return this.download(tag);
}
// upload the blob:
- // TODO uploadAudio -> uploadAudioVideo
const videoBlob = new Blob(this._videoBuffer);
- return this._psychoJS.serverManager.uploadAudio(videoBlob, tag);
+ return this._psychoJS.serverManager.uploadAudioVideo(videoBlob, tag);
}
@@ -399,22 +410,12 @@ export class Camera extends PsychObject
async getRecording({tag, flush = false} = {})
{
// default tag: the name of this Microphone object
- if (typeof tag === 'undefined')
+ if (typeof tag === "undefined")
{
tag = this._name;
}
// TODO
-/*
- const videoClip = new VideoClip({
- psychoJS: this._psychoJS,
- name: tag,
- format: this._format,
- data: new Blob(this._videoBuffer)
- });
-
- return videoClip;
-*/
}
@@ -455,16 +456,9 @@ export class Camera extends PsychObject
this._videos = [];
// create a new stream with ideal dimensions:
+ // TODO use size constraints
this._stream = await navigator.mediaDevices.getUserMedia({
video: true
- /*video: {
- width: {
- ideal: 640 //1920
- },
- height: {
- ideal: 480 //1080
- }
- }*/
});
// check the actual width and height:
@@ -474,7 +468,7 @@ export class Camera extends PsychObject
// check that the specified format is supported, use default if it is not:
let options;
- if (typeof this._format === 'string' && MediaRecorder.isTypeSupported(this._format))
+ if (typeof this._format === "string" && MediaRecorder.isTypeSupported(this._format))
{
options = { type: this._format };
}
@@ -499,7 +493,7 @@ export class Camera extends PsychObject
self._videoBuffer.length = 0;
self._clock.reset();
self._status = PsychoJS.Status.STARTED;
- self._psychoJS.logger.debug('video recording started');
+ self._psychoJS.logger.debug("video recording started");
// resolve the Microphone.start promise:
if (self._startCallback)
@@ -512,7 +506,7 @@ export class Camera extends PsychObject
this._recorder.onpause = () =>
{
self._status = PsychoJS.Status.PAUSED;
- self._psychoJS.logger.debug('video recording paused');
+ self._psychoJS.logger.debug("video recording paused");
// resolve the Microphone.pause promise:
if (self._pauseCallback)
@@ -525,7 +519,7 @@ export class Camera extends PsychObject
this._recorder.onresume = () =>
{
self._status = PsychoJS.Status.STARTED;
- self._psychoJS.logger.debug('video recording resumed');
+ self._psychoJS.logger.debug("video recording resumed");
// resolve the Microphone.resume promise:
if (self._resumeCallback)
@@ -541,7 +535,7 @@ export class Camera extends PsychObject
// add data to the buffer:
self._videoBuffer.push(data);
- self._psychoJS.logger.debug('video data added to the buffer');
+ self._psychoJS.logger.debug("video data added to the buffer");
// resolve the data available promise, if needed:
if (self._dataAvailableCallback)
@@ -553,7 +547,7 @@ export class Camera extends PsychObject
// called upon Camera.stop(), after data has been made available:
this._recorder.onstop = () =>
{
- self._psychoJS.logger.debug('video recording stopped');
+ self._psychoJS.logger.debug("video recording stopped");
self._status = PsychoJS.Status.NOT_STARTED;
// resolve the Microphone.stop promise:
@@ -565,7 +559,7 @@ export class Camera extends PsychObject
// treat stop options if there are any:
// download to a file, immediately offered to the participant:
- if (typeof self._stopOptions.filename === 'string')
+ if (typeof self._stopOptions.filename === "string")
{
self.download(self._stopOptions.filename);
}
@@ -575,7 +569,7 @@ export class Camera extends PsychObject
this._recorder.onerror = (event) =>
{
// TODO
- self._psychoJS.logger.error('video recording error: ' + JSON.stringify(event));
+ self._psychoJS.logger.error("video recording error: " + JSON.stringify(event));
self._status = PsychoJS.Status.ERROR;
};
diff --git a/src/visual/FaceDetector.js b/src/visual/FaceDetector.js
index c6143ad..559f244 100644
--- a/src/visual/FaceDetector.js
+++ b/src/visual/FaceDetector.js
@@ -7,17 +7,19 @@
* @license Distributed under the terms of the MIT License
*/
-import {PsychoJS} from "../core/PsychoJS";
-import * as util from '../util/Util';
-import {Color} from '../util/Color';
-import {Camera} from "./Camera";
-import {VisualStim} from "./VisualStim";
+import {PsychoJS} from "../core/PsychoJS.js";
+import * as util from "../util/Util.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import {Color} from "../util/Color.js";
+import {Camera} from "./Camera.js";
+import {VisualStim} from "./VisualStim.js";
import * as PIXI from "pixi.js-legacy";
/**
- * This manager handles the detecting of faces in video streams.
- * The detection is performed using the Face-API library: https://github.com/justadudewhohacks/face-api.js
+ * This manager handles the detecting of faces in video streams. FaceDetector relies on the
+ * [Face-API library]{@link https://github.com/justadudewhohacks/face-api.js} developed by
+ * [Vincent Muehler]{@link https://github.com/justadudewhohacks}
*
* @name module:visual.FaceDetector
* @class
@@ -39,17 +41,20 @@ import * as PIXI from "pixi.js-legacy";
*/
export class FaceDetector extends VisualStim
{
-
+ /**
+ * @constructor
+ * @public
+ */
constructor({name, win, input, modelDir, faceApiUrl, units, ori, opacity, pos, size, autoDraw, autoLog} = {})
{
super({name, win, units, ori, opacity, pos, size, autoDraw, autoLog});
// TODO deal with onChange (see MovieStim and Camera)
- this._addAttribute('input', input, undefined);
- this._addAttribute('faceApiUrl', faceApiUrl, 'face-api.js');
- this._addAttribute('modelDir', modelDir, 'models');
- this._addAttribute('autoLog', autoLog, false);
- this._addAttribute('status', PsychoJS.Status.NOT_STARTED);
+ this._addAttribute("input", input, undefined);
+ this._addAttribute("faceApiUrl", faceApiUrl, "face-api.js");
+ this._addAttribute("modelDir", modelDir, "models");
+ this._addAttribute("autoLog", autoLog, false);
+ this._addAttribute("status", PsychoJS.Status.NOT_STARTED);
// init face-api:
this._initFaceApi();
@@ -65,6 +70,7 @@ export class FaceDetector extends VisualStim
* Setter for the video attribute.
*
* @name module:visual.FaceDetector#setCamera
+ * @function
* @public
* @param {string | HTMLVideoElement | module:visual.Camera} input - the name of a
* movie resource or a HTMLVideoElement or a Camera component
@@ -73,23 +79,23 @@ export class FaceDetector extends VisualStim
setInput(input, log = false)
{
const response = {
- origin: 'FaceDetector.setInput',
- context: 'when setting the video of FaceDetector: ' + this._name
+ origin: "FaceDetector.setInput",
+ context: "when setting the video of FaceDetector: " + this._name
};
try
{
// movie is undefined: that's fine but we raise a warning in case this is
// a symptom of an actual problem
- if (typeof input === 'undefined')
+ if (typeof input === "undefined")
{
- this.psychoJS.logger.warn('setting the movie of MovieStim: ' + this._name + ' with argument: undefined.');
- this.psychoJS.logger.debug('set the movie of MovieStim: ' + this._name + ' as: undefined');
+ this.psychoJS.logger.warn("setting the movie of MovieStim: " + this._name + " with argument: undefined.");
+ this.psychoJS.logger.debug("set the movie of MovieStim: " + this._name + " as: undefined");
}
else
{
// if movie is a string, then it should be the name of a resource, which we get:
- if (typeof input === 'string')
+ if (typeof input === "string")
{
// TODO create a movie with that resource, and use the movie as input
}
@@ -106,7 +112,7 @@ export class FaceDetector extends VisualStim
// check that video is now an HTMLVideoElement
if (!(input instanceof HTMLVideoElement))
{
- throw input.toString() + ' is not a video';
+ throw input.toString() + " is not a video";
}
this.psychoJS.logger.debug(`set the video of FaceDetector: ${this._name} as: src= ${input.src}, size= ${input.videoWidth}x${input.videoHeight}, duration= ${input.duration}s`);
@@ -123,7 +129,7 @@ export class FaceDetector extends VisualStim
}
}
- this._setAttribute('input', input, log);
+ this._setAttribute("input", input, log);
this._needUpdate = true;
this._needPixiUpdate = true;
}
@@ -138,6 +144,7 @@ export class FaceDetector extends VisualStim
* Start detecting faces.
*
* @name module:visual.FaceDetector#start
+ * @function
* @public
* @param {number} period - the detection period, in ms (e.g. 100 ms for 10Hz)
* @param detectionCallback - the callback triggered when detection results are available
@@ -147,7 +154,7 @@ export class FaceDetector extends VisualStim
{
this.status = PsychoJS.Status.STARTED;
- if (typeof this._detectionId !== 'undefined')
+ if (typeof this._detectionId !== "undefined")
{
clearInterval(this._detectionId);
this._detectionId = undefined;
@@ -176,6 +183,7 @@ export class FaceDetector extends VisualStim
* Stop detecting faces.
*
* @name module:visual.FaceDetector#stop
+ * @function
* @public
* @param {boolean} [log= false] - whether of not to log
*/
@@ -183,7 +191,7 @@ export class FaceDetector extends VisualStim
{
this.status = PsychoJS.Status.NOT_STARTED;
- if (typeof this._detectionId !== 'undefined')
+ if (typeof this._detectionId !== "undefined")
{
clearInterval(this._detectionId);
this._detectionId = undefined;
@@ -195,16 +203,17 @@ export class FaceDetector extends VisualStim
* Init the Face-API library.
*
* @name module:visual.FaceDetector#_initFaceApi
- * @private
+ * @function
+ * @protected
*/
async _initFaceApi()
{/*
// load the library:
await this._psychoJS.serverManager.prepareResources([
{
- 'name': 'face-api.js',
- 'path': this.faceApiUrl,
- 'download': true
+ "name": "face-api.js",
+ "path": this.faceApiUrl,
+ "download": true
}
]);*/
@@ -220,7 +229,8 @@ export class FaceDetector extends VisualStim
* Update the visual representation of the detected faces, if necessary.
*
* @name module:visual.FaceDetector#_updateIfNeeded
- * @private
+ * @function
+ * @protected
*/
_updateIfNeeded()
{
@@ -234,7 +244,7 @@ export class FaceDetector extends VisualStim
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
@@ -246,7 +256,7 @@ export class FaceDetector extends VisualStim
this._pixi.addChild(this._body);
const size_px = util.to_px(this.size, this.units, this.win);
- if (typeof this._detections !== 'undefined')
+ if (typeof this._detections !== "undefined")
{
for (const detection of this._detections)
{
@@ -256,7 +266,7 @@ export class FaceDetector extends VisualStim
for (const position of landmarks.positions)
{
- this._body.beginFill(new Color('red').int, this._opacity);
+ this._body.beginFill(new Color("red").int, this._opacity);
this._body.drawCircle(
position._x / imageWidth * size_px[0] - size_px[0] / 2,
position._y / imageHeight * size_px[1] - size_px[1] / 2,
@@ -273,7 +283,7 @@ export class FaceDetector extends VisualStim
this._pixi.scale.y = -1;
this._pixi.rotation = this.ori * Math.PI / 180;
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.alpha = this._opacity;
}
@@ -290,15 +300,6 @@ export class FaceDetector extends VisualStim
_estimateBoundingBox()
{
// TODO
-
- /*this._boundingBox = new PIXI.Rectangle(
- this._pos[0] + this._getLengthUnits(limits_px[0]),
- this._pos[1] + this._getLengthUnits(limits_px[1]),
- this._getLengthUnits(limits_px[2] - limits_px[0]),
- this._getLengthUnits(limits_px[3] - limits_px[1])
- );*/
-
- // TODO take the orientation into account
}
}
diff --git a/src/visual/Form.js b/src/visual/Form.js
index c9cfb21..5d7001f 100644
--- a/src/visual/Form.js
+++ b/src/visual/Form.js
@@ -7,18 +7,16 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import * as util from '../util/Util';
-import {TrialHandler} from '../data/TrialHandler';
-import {TextStim} from './TextStim';
-import {TextBox} from './TextBox';
-import {VisualStim} from './VisualStim';
-import {Slider} from './Slider';
-
-
+import * as PIXI from "pixi.js-legacy";
+import { TrialHandler } from "../data/TrialHandler.js";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { Slider } from "./Slider.js";
+import { TextBox } from "./TextBox.js";
+import { TextStim } from "./TextStim.js";
+import { VisualStim } from "./VisualStim.js";
/**
* Form stimulus.
@@ -57,99 +55,127 @@ import {Slider} from './Slider';
*/
export class Form extends util.mix(VisualStim).with(ColorMixin)
{
- constructor({name, win, pos, size, units, borderColor, fillColor, itemColor, markerColor, responseColor, color, contrast, opacity, depth, items, randomize, itemPadding, font, fontFamily, bold, italic, fontSize, clipMask, autoDraw, autoLog} = {})
+ constructor(
+ {
+ name,
+ win,
+ pos,
+ size,
+ units,
+ borderColor,
+ fillColor,
+ itemColor,
+ markerColor,
+ responseColor,
+ color,
+ contrast,
+ opacity,
+ depth,
+ items,
+ randomize,
+ itemPadding,
+ font,
+ fontFamily,
+ bold,
+ italic,
+ fontSize,
+ clipMask,
+ autoDraw,
+ autoLog,
+ } = {},
+ )
{
- super({name, win, units, opacity, depth, pos, size, clipMask, autoDraw, autoLog});
+ super({ name, win, units, opacity, depth, pos, size, clipMask, autoDraw, autoLog });
this._addAttribute(
- 'itemPadding',
+ "itemPadding",
itemPadding,
- util.to_unit([20, 0], 'pix', win, this._units)[0],
- this._onChange(true, false)
+ util.to_unit([20, 0], "pix", win, this._units)[0],
+ this._onChange(true, false),
);
// colors:
this._addAttribute(
- 'color',
+ "color",
// Same as itemColor
color,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'borderColor',
+ "borderColor",
borderColor,
fillColor,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'fillColor',
+ "fillColor",
fillColor,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'itemColor',
+ "itemColor",
itemColor,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'markerColor',
+ "markerColor",
markerColor,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'responseColor',
+ "responseColor",
responseColor,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
// fonts:
this._addAttribute(
- 'font',
+ "font",
font,
- 'Arial',
- this._onChange(true, true)
+ "Arial",
+ this._onChange(true, true),
);
// Not in use at present
this._addAttribute(
- 'fontFamily',
+ "fontFamily",
fontFamily,
- 'Helvetica',
- this._onChange(true, true)
+ "Helvetica",
+ this._onChange(true, true),
);
this._addAttribute(
- 'fontSize',
+ "fontSize",
fontSize,
- (this._units === 'pix') ? 14 : 0.03,
- this._onChange(true, true)
+ (this._units === "pix") ? 14 : 0.03,
+ this._onChange(true, true),
);
this._addAttribute(
- 'bold',
+ "bold",
bold,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'italic',
+ "italic",
italic,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
// callback to deal with changes to items:
@@ -165,16 +191,17 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
};
this._addAttribute(
- 'items',
+ "items",
items,
[],
- onItemChange);
+ onItemChange,
+ );
this._addAttribute(
- 'randomize',
+ "randomize",
randomize,
false,
- onItemChange);
-
+ onItemChange,
+ );
this._scrollbarWidth = 0.02;
this._responseTextHeightRatio = 0.8;
@@ -191,8 +218,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Force a refresh of the stimulus.
*
@@ -217,8 +242,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Overridden draw that also calls the draw method of all form elements.
*
@@ -259,8 +282,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._scrollbar.draw();
}
-
-
/**
* Overridden hide that also calls the hide method of all form elements.
*
@@ -275,7 +296,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
super.hide();
// hide the stimuli:
- if (typeof this._items !== 'undefined')
+ if (typeof this._items !== "undefined")
{
for (let i = 0; i < this._items.length; ++i)
{
@@ -297,8 +318,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Reset the form.
*
@@ -308,7 +327,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
*/
reset()
{
- this.psychoJS.logger.debug('reset Form: ', this._name);
+ this.psychoJS.logger.debug("reset Form: ", this._name);
// reset the stimuli:
for (let i = 0; i < this._items.length; ++i)
@@ -326,8 +345,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._needUpdate = true;
}
-
-
/**
* Collate the questions and responses into a single dataset.
*
@@ -351,9 +368,9 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
item.response = responseStim.getRating();
item.rt = responseStim.getRT();
- if (typeof item.response === 'undefined')
+ if (typeof item.response === "undefined")
{
- ++ nbIncompleteResponse;
+ ++nbIncompleteResponse;
}
}
else if (item.type === Form.Types.FREE_TEXT)
@@ -363,7 +380,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
if (item.response.length === 0)
{
- ++ nbIncompleteResponse;
+ ++nbIncompleteResponse;
}
}
}
@@ -371,9 +388,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._items._complete = (nbIncompleteResponse === 0);
-
// return a copy of this._items:
- return this._items.map(item => Object.assign({}, item));
+ return this._items.map((item) => Object.assign({}, item));
}
/**
* Check if the form is complete.
@@ -385,7 +401,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
*/
formComplete()
{
- //same as complete but might be used by some experiments before 2020.2
+ // same as complete but might be used by some experiments before 2020.2
this.getData();
return this._items._complete;
}
@@ -398,15 +414,21 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
* @param {module:data.ExperimentHandler} experiment - the experiment into which to insert the form data
* @param {string} [format= 'rows'] - whether to insert the data as rows or as columns
*/
- addDataToExp(experiment, format = 'rows')
+ addDataToExp(experiment, format = "rows")
{
- const addAsColumns = ['cols', 'columns'].includes(format.toLowerCase());
+ const addAsColumns = ["cols", "columns"].includes(format.toLowerCase());
const data = this.getData();
const _doNotSave = [
- 'itemCtrl', 'responseCtrl',
- 'itemColor', 'options', 'ticks', 'tickLabels',
- 'responseWidth', 'responseColor', 'layout'
+ "itemCtrl",
+ "responseCtrl",
+ "itemColor",
+ "options",
+ "ticks",
+ "tickLabels",
+ "responseWidth",
+ "responseColor",
+ "layout",
];
for (const item of this.getData())
@@ -419,7 +441,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const columnName = (addAsColumns) ? `${this._name}[${index}]${field}` : `${this._name}${field}`;
experiment.addData(columnName, item[field]);
}
- ++ index;
+ ++index;
}
if (!addAsColumns)
@@ -434,8 +456,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Import and process the form items from either a spreadsheet resource files (.csv, .xlsx, etc.) or from an array.
*
@@ -446,8 +466,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
_processItems()
{
const response = {
- origin: 'Form._processItems',
- context: 'when processing the form items'
+ origin: "Form._processItems",
+ context: "when processing the form items",
};
try
@@ -455,7 +475,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
if (this._autoLog)
{
// note: we use the same log message as PsychoPy even though we called this method differently
- this._psychoJS.experimentLogger.exp('Importing items...');
+ this._psychoJS.experimentLogger.exp("Importing items...");
}
// import the items:
@@ -473,12 +493,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
catch (error)
{
// throw { ...response, error };
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Import the form items from either a spreadsheet resource files (.csv, .xlsx, etc.) or from an array.
*
@@ -489,8 +507,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
_importItems()
{
const response = {
- origin: 'Form._importItems',
- context: 'when importing the form items'
+ origin: "Form._importItems",
+ context: "when importing the form items",
};
try
@@ -498,17 +516,15 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const itemsType = typeof this._items;
// we treat undefined items as a list with a single default entry:
- if (itemsType === 'undefined')
+ if (itemsType === "undefined")
{
this._items = [Form._defaultItems];
}
-
// if items is a string, we treat it as the name of a resource file and import it:
- else if (itemsType === 'string')
+ else if (itemsType === "string")
{
this._items = TrialHandler.importConditions(this._psychoJS.serverManager, this._items);
}
-
// unknown items type:
else
{
@@ -520,17 +536,14 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
{
this._items = [Form._defaultItems];
}
-
}
catch (error)
{
// throw { ...response, error };
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Sanitize the form items: check that the keys are valid, and fill in default values.
*
@@ -541,8 +554,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
_sanitizeItems()
{
const response = {
- origin: 'Form._sanitizeItems',
- context: 'when sanitizing the form items'
+ origin: "Form._sanitizeItems",
+ context: "when sanitizing the form items",
};
try
@@ -551,7 +564,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
for (const item of this._items)
{
// old style forms have questionText instead of itemText:
- if (typeof item.questionText !== 'undefined')
+ if (typeof item.questionText !== "undefined")
{
item.itemText = item.questionText;
delete item.questionText;
@@ -560,12 +573,11 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
delete item.questionWidth;
// for items of type 'rating, the ticks are in 'options' instead of in 'ticks':
- if (item.type === 'rating' || item.type === 'slider')
+ if (item.type === "rating" || item.type === "slider")
{
item.ticks = item.options;
item.options = undefined;
}
-
}
}
@@ -583,9 +595,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
missingKeys.add(key);
item[key] = Form._defaultItems[key];
}
-
// undefined value:
- else if (typeof item[key] === 'undefined')
+ else if (typeof item[key] === "undefined")
{
// TODO: options = '' for FREE_TEXT
item[key] = Form._defaultItems[key];
@@ -595,16 +606,17 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
if (missingKeys.size > 0)
{
- this._psychoJS.logger.warn(`Missing headers: ${Array.from(missingKeys).join(', ')}\nNote, headers are case sensitive and must match: ${Array.from(defaultKeys).join(', ')}`);
+ this._psychoJS.logger.warn(
+ `Missing headers: ${Array.from(missingKeys).join(", ")}\nNote, headers are case sensitive and must match: ${Array.from(defaultKeys).join(", ")}`,
+ );
}
-
// check the types and options:
const formTypes = Object.getOwnPropertyNames(Form.Types);
for (const item of this._items)
{
// convert type to upper case, replace spaces by underscores
- item.type = item.type.toUpperCase().replace(' ', '_');
+ item.type = item.type.toUpperCase().replace(" ", "_");
// check that the type is valid:
if (!formTypes.includes(item.type))
@@ -613,9 +625,9 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
// Support the 'radio' type found on older versions of PsychoPy
- if (item.type === 'RADIO')
+ if (item.type === "RADIO")
{
- item.type = 'CHOICE';
+ item.type = "CHOICE";
}
// convert item type to symbol:
@@ -624,18 +636,17 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// turn the option into an array and check length, where applicable:
if (item.type === Form.Types.CHOICE)
{
- item.options = item.options.split(',');
+ item.options = item.options.split(",");
if (item.options.length < 2)
{
throw `at least two choices should be provided for choice item: ${item.itemText}`;
}
}
-
// turn the ticks and tickLabels into arrays, where applicable:
else if (item.type === Form.Types.RATING || item.type === Form.Types.SLIDER)
{
- item.ticks = item.ticks.split(',').map( (_,t) => parseInt(t) );
- item.tickLabels = (item.tickLabels.length > 0) ? item.tickLabels.split(',') : [];
+ item.ticks = item.ticks.split(",").map((_, t) => parseInt(t));
+ item.tickLabels = (item.tickLabels.length > 0) ? item.tickLabels.split(",") : [];
}
// TODO
@@ -644,7 +655,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
// check the layout:
- const formLayouts = ['HORIZ', 'VERT'];
+ const formLayouts = ["HORIZ", "VERT"];
for (const item of this._items)
{
// convert layout to upper case:
@@ -657,18 +668,16 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
// convert item layout to symbol:
- item.layout = (item.layout === 'HORIZ') ? Form.Layout.HORIZONTAL : Form.Layout.VERTICAL;
+ item.layout = (item.layout === "HORIZ") ? Form.Layout.HORIZONTAL : Form.Layout.VERTICAL;
}
}
catch (error)
{
// throw { ...response, error };
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Estimate the bounding box.
*
@@ -684,12 +693,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._pos[0] - this._size[0] / 2.0,
this._pos[1] - this._size[1] / 2.0,
this._size[0],
- this._size[1]
+ this._size[1],
);
}
-
-
/**
* Setup the stimuli, and the scrollbar.
*
@@ -705,7 +712,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
// clean up the previously setup stimuli:
- if (typeof this._visual !== 'undefined')
+ if (typeof this._visual !== "undefined")
{
for (const textStim of this._visual.textStims)
{
@@ -723,31 +730,30 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
textStims: [],
responseStims: [],
visibles: [],
- stimuliTotalHeight: 0
+ stimuliTotalHeight: 0,
};
// instantiate the clip mask that will be used by all stimuli:
this._stimuliClipMask = new PIXI.Graphics();
-
// default stimulus options:
const textStimOption = {
win: this._win,
- name: 'item text',
+ name: "item text",
font: this.font,
units: this._units,
- alignHoriz: 'left',
- alignVert: 'top',
+ alignHoriz: "left",
+ alignVert: "top",
height: this._fontSize,
color: this.itemColor,
ori: 0,
opacity: 1,
depth: this._depth + 1,
- clipMask: this._stimuliClipMask
+ clipMask: this._stimuliClipMask,
};
const sliderOption = {
win: this._win,
- name: 'choice response',
+ name: "choice response",
units: this._units,
flip: false,
// Not part of Slider options as things stand
@@ -762,13 +768,13 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
opacity: 1,
depth: this._depth + 1,
clipMask: this._stimuliClipMask,
- granularity: 1
+ granularity: 1,
};
const textBoxOption = {
win: this._win,
- name: 'free text response',
+ name: "free text response",
units: this._units,
- anchor: 'left-top',
+ anchor: "left-top",
flip: false,
opacity: 1,
depth: this._depth + 1,
@@ -776,7 +782,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
letterHeight: this._fontSize * this._responseTextHeightRatio,
bold: false,
italic: false,
- alignment: 'left',
+ alignment: "left",
color: this.responseColor,
fillColor: this.fillColor,
contrast: 1.0,
@@ -784,17 +790,16 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
borderWidth: 0.002,
padding: 0.01,
editable: true,
- clipMask: this._stimuliClipMask
+ clipMask: this._stimuliClipMask,
};
// we use for the slider's tick size the height of a word:
- const textStim = new TextStim(Object.assign(textStimOption, { text: 'Ag', pos: [0, 0]}));
+ const textStim = new TextStim(Object.assign(textStimOption, { text: "Ag", pos: [0, 0] }));
const textMetrics_px = textStim.getTextMetrics();
const sliderTickSize = this._getLengthUnits(textMetrics_px.height) / 2;
textStim.release(false);
-
- let stimulusOffset = - this._itemPadding;
+ let stimulusOffset = -this._itemPadding;
for (const item of this._items)
{
// initially, all items are invisible:
@@ -805,8 +810,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// - description: + - + + = this._size[0]
// - choice with vert layout: +
- + + = this._size[0]
let rowWidth;
- if (item.type === Form.Types.HEADING || item.type === Form.Types.DESCRIPTION ||
- (item.type === Form.Types.CHOICE && item.layout === Form.Layout.VERTICAL))
+ if (
+ item.type === Form.Types.HEADING || item.type === Form.Types.DESCRIPTION
+ || (item.type === Form.Types.CHOICE && item.layout === Form.Layout.VERTICAL)
+ )
{
rowWidth = (this._size[0] - this._itemPadding * 2 - this._scrollbarWidth);
}
@@ -817,12 +824,13 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
// item text
- const itemWidth = rowWidth * item.itemWidth;
+ const itemWidth = rowWidth * item.itemWidth;
const textStim = new TextStim(
Object.assign(textStimOption, {
text: item.itemText,
- wrapWidth: itemWidth
- }));
+ wrapWidth: itemWidth,
+ }),
+ );
textStim._relativePos = [this._itemPadding, stimulusOffset];
const textHeight = textStim.boundingBox.height;
this._visual.textStims.push(textStim);
@@ -846,7 +854,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
else
{
- sliderSize = [sliderTickSize, (sliderTickSize*1.5) * item.options.length];
+ sliderSize = [sliderTickSize, (sliderTickSize * 1.5) * item.options.length];
compact = false;
flip = true;
}
@@ -881,23 +889,23 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
labels,
ticks,
compact,
- flip
- })
+ flip,
+ }),
);
responseHeight = responseStim.boundingBox.height;
if (item.layout === Form.Layout.HORIZONTAL)
{
responseStim._relativePos = [
this._itemPadding * 2 + itemWidth + responseWidth / 2,
- stimulusOffset
- //- Math.max(0, (textHeight - responseHeight) / 2) // (vertical centering)
+ stimulusOffset,
+ // - Math.max(0, (textHeight - responseHeight) / 2) // (vertical centering)
];
}
else
{
responseStim._relativePos = [
- this._itemPadding * 2 + itemWidth, //this._itemPadding + sliderTickSize,
- stimulusOffset - responseHeight / 2 - textHeight - this._itemPadding
+ this._itemPadding * 2 + itemWidth, // this._itemPadding + sliderTickSize,
+ stimulusOffset - responseHeight / 2 - textHeight - this._itemPadding,
];
// since rowHeight will be the max of itemHeight and responseHeight, we need to alter responseHeight
@@ -905,20 +913,19 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
responseHeight += textHeight + this._itemPadding;
}
}
-
// FREE TEXT
else if (item.type === Form.Types.FREE_TEXT)
{
responseStim = new TextBox(
Object.assign(textBoxOption, {
text: item.options,
- size: [responseWidth, -1]
- })
+ size: [responseWidth, -1],
+ }),
);
responseHeight = responseStim.boundingBox.height;
responseStim._relativePos = [
this._itemPadding * 2 + itemWidth,
- stimulusOffset
+ stimulusOffset,
];
}
@@ -931,13 +938,12 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
}
this._visual.stimuliTotalHeight = stimulusOffset;
-
// scrollbar
// note: we add this Form as a dependent stimulus such that the Form is redrawn whenever
// the slider is updated
this._scrollbar = new Slider({
win: this._win,
- name: 'scrollbar',
+ name: "scrollbar",
units: this._units,
color: this.itemColor,
depth: this._depth + 1,
@@ -945,24 +951,20 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
size: [this._scrollbarWidth, this._size[1]],
style: [Slider.Style.SLIDER],
ticks: [0, -this._visual.stimuliTotalHeight / this._size[1]],
- dependentStims: [this]
+ dependentStims: [this],
});
this._prevScrollbarMarkerPos = 0;
this._scrollbar.setMarkerPos(this._prevScrollbarMarkerPos);
-
// estimate the bounding box:
this._estimateBoundingBox();
-
if (this._autoLog)
{
this._psychoJS.experimentLogger.exp(`Layout set for: ${this.name}`);
}
}
-
-
/**
* Update the form visual representation, if necessary.
*
@@ -990,17 +992,18 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
[this._leftEdge, this._topEdge],
this.units,
this.win,
- true);
+ true,
+ );
[this._rightEdge_px, this._bottomEdge_px] = util.to_px(
[this._rightEdge, this._bottomEdge],
this.units,
this.win,
- true);
+ true,
+ );
this._itemPadding_px = this._getLengthPix(this._itemPadding);
this._scrollbarWidth_px = this._getLengthPix(this._scrollbarWidth, true);
this._size_px = util.to_px(this._size, this.units, this.win, true);
-
// update the stimuli clip mask
// note: the clip mask is in screen coordinates
this._stimuliClipMask.clear();
@@ -1009,11 +1012,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._win._rootContainer.position.x + this._leftEdge_px + 2,
this._win._rootContainer.position.y + this._bottomEdge_px + 2,
this._size_px[0] - 4,
- this._size_px[1] - 6
+ this._size_px[1] - 6,
);
this._stimuliClipMask.endFill();
-
// position the scrollbar and get the scrollbar offset, in form units:
this._scrollbar.setPos([this._rightEdge - this._scrollbarWidth / 2, this._pos[1]], false);
this._scrollbar.setOpacity(0.5);
@@ -1024,8 +1026,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._updateDecorations();
}
-
-
/**
* Update the visible stimuli.
*
@@ -1041,7 +1041,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const textStim = this._visual.textStims[i];
const textStimPos = [
this._leftEdge + textStim._relativePos[0],
- this._topEdge + textStim._relativePos[1] - this._scrollbarOffset
+ this._topEdge + textStim._relativePos[1] - this._scrollbarOffset,
];
textStim.setPos(textStimPos);
@@ -1051,7 +1051,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
{
const responseStimPos = [
this._leftEdge + responseStim._relativePos[0],
- this._topEdge + responseStim._relativePos[1] - this._scrollbarOffset
+ this._topEdge + responseStim._relativePos[1] - this._scrollbarOffset,
];
responseStim.setPos(responseStimPos);
}
@@ -1077,11 +1077,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._visual.visibles[i] = false;
}
}
-
}
-
-
/**
* Update the form decorations (bounding box, lines between items, etc.)
*
@@ -1091,7 +1088,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
*/
_updateDecorations()
{
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
@@ -1100,7 +1097,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._pixi.scale.x = 1;
this._pixi.scale.y = 1;
this._pixi.rotation = 0;
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.alpha = this._opacity;
this._pixi.zIndex = this._depth;
@@ -1108,7 +1105,6 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// apply the form clip mask (n.b., that is not the stimuli clip mask):
this._pixi.mask = this._clipMask;
-
// form background:
this._pixi.lineStyle(1, new Color(this.borderColor).int, this._opacity, 0.5);
// this._decorations.beginFill(this._barFillColor.int, this._opacity);
@@ -1121,7 +1117,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
this._decorations = new PIXI.Graphics();
this._pixi.addChild(this._decorations);
this._decorations.mask = this._stimuliClipMask;
- this._decorations.lineStyle(1, new Color('gray').int, this._opacity, 0.5);
+ this._decorations.lineStyle(1, new Color("gray").int, this._opacity, 0.5);
this._decorations.alpha = 0.5;
for (let i = 0; i < this._items.length; ++i)
@@ -1135,27 +1131,23 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const textStim = this._visual.textStims[i];
const textStimPos = [
this._leftEdge + textStim._relativePos[0],
- this._topEdge + textStim._relativePos[1] - this._scrollbarOffset
+ this._topEdge + textStim._relativePos[1] - this._scrollbarOffset,
];
const textStimPos_px = util.to_px(textStimPos, this._units, this._win);
- this._decorations.beginFill(new Color('darkgray').int);
+ this._decorations.beginFill(new Color("darkgray").int);
this._decorations.drawRect(
textStimPos_px[0] - this._itemPadding_px / 2,
textStimPos_px[1] + this._itemPadding_px / 2,
this._size_px[0] - this._itemPadding_px - this._scrollbarWidth_px,
- -this._getLengthPix(this._visual.rowHeights[i]) - this._itemPadding_px
+ -this._getLengthPix(this._visual.rowHeights[i]) - this._itemPadding_px,
);
this._decorations.endFill();
}
}
}
-
-
}
}
-
-
/**
* Form item types.
*
@@ -1164,17 +1156,15 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
* @public
*/
Form.Types = {
- HEADING: Symbol.for('HEADING'),
- DESCRIPTION: Symbol.for('DESCRIPTION'),
- RATING: Symbol.for('RATING'),
- SLIDER: Symbol.for('SLIDER'),
- FREE_TEXT: Symbol.for('FREE_TEXT'),
- CHOICE: Symbol.for('CHOICE'),
- RADIO: Symbol.for('RADIO')
+ HEADING: Symbol.for("HEADING"),
+ DESCRIPTION: Symbol.for("DESCRIPTION"),
+ RATING: Symbol.for("RATING"),
+ SLIDER: Symbol.for("SLIDER"),
+ FREE_TEXT: Symbol.for("FREE_TEXT"),
+ CHOICE: Symbol.for("CHOICE"),
+ RADIO: Symbol.for("RADIO"),
};
-
-
/**
* Form item layout.
*
@@ -1183,12 +1173,10 @@ Form.Types = {
* @public
*/
Form.Layout = {
- HORIZONTAL: Symbol.for('HORIZONTAL'),
- VERTICAL: Symbol.for('VERTICAL')
+ HORIZONTAL: Symbol.for("HORIZONTAL"),
+ VERTICAL: Symbol.for("VERTICAL"),
};
-
-
/**
* Default form item.
*
@@ -1197,18 +1185,16 @@ Form.Layout = {
*
*/
Form._defaultItems = {
- 'itemText': 'Default question',
- 'type': 'rating',
- 'options': 'Yes, No',
- 'tickLabels': '',
- 'itemWidth': 0.7,
- 'itemColor': 'white',
+ "itemText": "Default question",
+ "type": "rating",
+ "options": "Yes, No",
+ "tickLabels": "",
+ "itemWidth": 0.7,
+ "itemColor": "white",
- 'responseWidth': 0.3,
- 'responseColor': 'white',
+ "responseWidth": 0.3,
+ "responseColor": "white",
- 'index': 0,
- 'layout': 'horiz'
+ "index": 0,
+ "layout": "horiz",
};
-
-
diff --git a/src/visual/ImageStim.js b/src/visual/ImageStim.js
index d74b3bd..29c542a 100644
--- a/src/visual/ImageStim.js
+++ b/src/visual/ImageStim.js
@@ -7,13 +7,12 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import * as util from '../util/Util';
-
+import * as PIXI from "pixi.js-legacy";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { VisualStim } from "./VisualStim.js";
/**
* Image Stimulus.
@@ -45,53 +44,53 @@ import * as util from '../util/Util';
*/
export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
{
- constructor({name, win, image, mask, pos, units, ori, size, color, opacity, contrast, texRes, depth, interpolate, flipHoriz, flipVert, autoDraw, autoLog} = {})
+ constructor({ name, win, image, mask, pos, units, ori, size, color, opacity, contrast, texRes, depth, interpolate, flipHoriz, flipVert, autoDraw, autoLog } = {})
{
- super({name, win, units, ori, opacity, depth, pos, size, autoDraw, autoLog});
+ super({ name, win, units, ori, opacity, depth, pos, size, autoDraw, autoLog });
this._addAttribute(
- 'image',
- image
+ "image",
+ image,
);
this._addAttribute(
- 'mask',
- mask
+ "mask",
+ mask,
);
this._addAttribute(
- 'color',
+ "color",
color,
- 'white',
- this._onChange(true, false)
+ "white",
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'texRes',
+ "texRes",
texRes,
128,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'interpolate',
+ "interpolate",
interpolate,
false,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'flipHoriz',
+ "flipHoriz",
flipHoriz,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'flipVert',
+ "flipVert",
flipVert,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
// estimate the bounding box:
@@ -103,8 +102,6 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Setter for the image attribute.
*
@@ -116,22 +113,22 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
setImage(image, log = false)
{
const response = {
- origin: 'ImageStim.setImage',
- context: 'when setting the image of ImageStim: ' + this._name
+ origin: "ImageStim.setImage",
+ context: "when setting the image of ImageStim: " + this._name,
};
try
{
// image is undefined: that's fine but we raise a warning in case this is a symptom of an actual problem
- if (typeof image === 'undefined')
+ if (typeof image === "undefined")
{
- this.psychoJS.logger.warn('setting the image of ImageStim: ' + this._name + ' with argument: undefined.');
- this.psychoJS.logger.debug('set the image of ImageStim: ' + this._name + ' as: undefined');
+ this.psychoJS.logger.warn("setting the image of ImageStim: " + this._name + " with argument: undefined.");
+ this.psychoJS.logger.debug("set the image of ImageStim: " + this._name + " as: undefined");
}
else
{
// image is a string: it should be the name of a resource, which we load
- if (typeof image === 'string')
+ if (typeof image === "string")
{
image = this.psychoJS.serverManager.getResource(image);
}
@@ -139,16 +136,16 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
// image should now be an actual HTMLImageElement: we raise an error if it is not
if (!(image instanceof HTMLImageElement))
{
- throw 'the argument: ' + image.toString() + ' is not an image" }';
+ throw "the argument: " + image.toString() + ' is not an image" }';
}
- this.psychoJS.logger.debug('set the image of ImageStim: ' + this._name + ' as: src= ' + image.src + ', size= ' + image.width + 'x' + image.height);
+ this.psychoJS.logger.debug("set the image of ImageStim: " + this._name + " as: src= " + image.src + ", size= " + image.width + "x" + image.height);
}
const existingImage = this.getImage();
const hasChanged = existingImage ? existingImage.src !== image.src : true;
- this._setAttribute('image', image, log);
+ this._setAttribute("image", image, log);
if (hasChanged)
{
@@ -157,12 +154,10 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Setter for the mask attribute.
*
@@ -174,22 +169,22 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
setMask(mask, log = false)
{
const response = {
- origin: 'ImageStim.setMask',
- context: 'when setting the mask of ImageStim: ' + this._name
+ origin: "ImageStim.setMask",
+ context: "when setting the mask of ImageStim: " + this._name,
};
try
{
// mask is undefined: that's fine but we raise a warning in case this is a sympton of an actual problem
- if (typeof mask === 'undefined')
+ if (typeof mask === "undefined")
{
- this.psychoJS.logger.warn('setting the mask of ImageStim: ' + this._name + ' with argument: undefined.');
- this.psychoJS.logger.debug('set the mask of ImageStim: ' + this._name + ' as: undefined');
+ this.psychoJS.logger.warn("setting the mask of ImageStim: " + this._name + " with argument: undefined.");
+ this.psychoJS.logger.debug("set the mask of ImageStim: " + this._name + " as: undefined");
}
else
{
// mask is a string: it should be the name of a resource, which we load
- if (typeof mask === 'string')
+ if (typeof mask === "string")
{
mask = this.psychoJS.serverManager.getResource(mask);
}
@@ -197,24 +192,22 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
// mask should now be an actual HTMLImageElement: we raise an error if it is not
if (!(mask instanceof HTMLImageElement))
{
- throw 'the argument: ' + mask.toString() + ' is not an image" }';
+ throw "the argument: " + mask.toString() + ' is not an image" }';
}
- this.psychoJS.logger.debug('set the mask of ImageStim: ' + this._name + ' as: src= ' + mask.src + ', size= ' + mask.width + 'x' + mask.height);
+ this.psychoJS.logger.debug("set the mask of ImageStim: " + this._name + " as: src= " + mask.src + ", size= " + mask.width + "x" + mask.height);
}
- this._setAttribute('mask', mask, log);
+ this._setAttribute("mask", mask, log);
this._onChange(true, false)();
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Estimate the bounding box.
*
@@ -226,21 +219,19 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
_estimateBoundingBox()
{
const size = this._getDisplaySize();
- if (typeof size !== 'undefined')
+ if (typeof size !== "undefined")
{
this._boundingBox = new PIXI.Rectangle(
this._pos[0] - size[0] / 2,
this._pos[1] - size[1] / 2,
size[0],
- size[1]
+ size[1],
);
}
// TODO take the orientation into account
}
-
-
/**
* Update the stimulus, if necessary.
*
@@ -260,14 +251,14 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
this._pixi = undefined;
// no image to draw: return immediately
- if (typeof this._image === 'undefined')
+ if (typeof this._image === "undefined")
{
return;
}
@@ -278,7 +269,7 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
this._pixi = PIXI.Sprite.from(this._texture);
// add a mask if need be:
- if (typeof this._mask !== 'undefined')
+ if (typeof this._mask !== "undefined")
{
this._pixi.mask = PIXI.Sprite.from(this._mask);
@@ -320,7 +311,7 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
this._pixi.scale.y = this.flipVert ? scaleY : -scaleY;
// set the position, rotation, and anchor (image centered on pos):
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.rotation = this.ori * Math.PI / 180;
this._pixi.anchor.x = 0.5;
this._pixi.anchor.y = 0.5;
@@ -329,8 +320,6 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
this._estimateBoundingBox();
}
-
-
/**
* Get the size of the display image, which is either that of the ImageStim or that of the image
* it contains.
@@ -343,18 +332,16 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
{
let displaySize = this.size;
- if (typeof displaySize === 'undefined')
+ if (typeof displaySize === "undefined")
{
// use the size of the texture, if we have access to it:
- if (typeof this._texture !== 'undefined' && this._texture.width > 0)
+ if (typeof this._texture !== "undefined" && this._texture.width > 0)
{
const textureSize = [this._texture.width, this._texture.height];
- displaySize = util.to_unit(textureSize, 'pix', this.win, this.units);
+ displaySize = util.to_unit(textureSize, "pix", this.win, this.units);
}
}
return displaySize;
}
-
-
}
diff --git a/src/visual/MovieStim.js b/src/visual/MovieStim.js
index 146f935..89a5fa3 100644
--- a/src/visual/MovieStim.js
+++ b/src/visual/MovieStim.js
@@ -7,13 +7,14 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import * as util from '../util/Util';
-import {PsychoJS} from "../core/PsychoJS";
-import {Camera} from "./Camera";
+import * as PIXI from "pixi.js-legacy";
+import { PsychoJS } from "../core/PsychoJS.js";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { VisualStim } from "./VisualStim.js";
+import {Camera} from "./Camera.js";
/**
@@ -49,82 +50,81 @@ import {Camera} from "./Camera";
*/
export class MovieStim extends VisualStim
{
- constructor({name, win, movie, pos, units, ori, size, color, opacity, contrast, interpolate, flipHoriz, flipVert, loop, volume, noAudio, autoPlay, autoDraw, autoLog} = {})
+ constructor({ name, win, movie, pos, units, ori, size, color, opacity, contrast, interpolate, flipHoriz, flipVert, loop, volume, noAudio, autoPlay, autoDraw, autoLog } = {})
{
- super({name, win, units, ori, opacity, pos, size, autoDraw, autoLog});
+ super({ name, win, units, ori, opacity, pos, size, autoDraw, autoLog });
- this.psychoJS.logger.debug('create a new MovieStim with name: ', name);
+ this.psychoJS.logger.debug("create a new MovieStim with name: ", name);
// movie and movie control:
this._addAttribute(
- 'movie',
- movie
+ "movie",
+ movie,
);
this._addAttribute(
- 'volume',
+ "volume",
volume,
1.0,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'noAudio',
+ "noAudio",
noAudio,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'autoPlay',
+ "autoPlay",
autoPlay,
true,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'flipHoriz',
+ "flipHoriz",
flipHoriz,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'flipVert',
+ "flipVert",
flipVert,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'interpolate',
+ "interpolate",
interpolate,
false,
- this._onChange(true, false)
+ this._onChange(true, false),
);
// colors:
this._addAttribute(
- 'color',
+ "color",
color,
- 'white',
- this._onChange(true, false)
+ "white",
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'loop',
+ "loop",
loop,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
-
// estimate the bounding box:
this._estimateBoundingBox();
// check whether the fastSeek method on HTMLVideoElement is implemented:
- const videoElement = document.createElement('video');
- this._hasFastSeek = (typeof videoElement.fastSeek === 'function');
+ const videoElement = document.createElement("video");
+ this._hasFastSeek = (typeof videoElement.fastSeek === "function");
if (this._autoLog)
{
@@ -132,8 +132,6 @@ export class MovieStim extends VisualStim
}
}
-
-
/**
* Setter for the movie attribute.
*
@@ -146,8 +144,8 @@ export class MovieStim extends VisualStim
setMovie(movie, log = false)
{
const response = {
- origin: 'MovieStim.setMovie',
- context: 'when setting the movie of MovieStim: ' + this._name
+ origin: "MovieStim.setMovie",
+ context: "when setting the movie of MovieStim: " + this._name,
};
try
@@ -164,7 +162,7 @@ export class MovieStim extends VisualStim
else
{
// if movie is a string, then it should be the name of a resource, which we get:
- if (typeof movie === 'string')
+ if (typeof movie === "string")
{
movie = this.psychoJS.serverManager.getResource(movie);
}
@@ -181,7 +179,7 @@ export class MovieStim extends VisualStim
// check that movie is now an HTMLVideoElement
if (!(movie instanceof HTMLVideoElement))
{
- throw movie.toString() + ' is not a video';
+ throw movie.toString() + " is not a video";
}
this.psychoJS.logger.debug(`set the movie of MovieStim: ${this._name} as: src= ${movie.src}, size= ${movie.videoWidth}x${movie.videoHeight}, duration= ${movie.duration}s`);
@@ -198,19 +196,16 @@ export class MovieStim extends VisualStim
}
}
-
- this._setAttribute('movie', movie, log);
+ this._setAttribute("movie", movie, log);
this._needUpdate = true;
this._needPixiUpdate = true;
}
catch (error)
{
- throw Object.assign(response, {error});
+ throw Object.assign(response, { error });
}
}
-
-
/**
* Reset the stimulus.
*
@@ -223,8 +218,6 @@ export class MovieStim extends VisualStim
this.seek(0, log);
}
-
-
/**
* Start playing the movie.
*
@@ -239,18 +232,17 @@ export class MovieStim extends VisualStim
if (playPromise !== undefined)
{
- playPromise.catch((error) => {
+ playPromise.catch((error) =>
+ {
throw {
- origin: 'MovieStim.play',
+ origin: "MovieStim.play",
context: `when attempting to play MovieStim: ${this._name}`,
- error
+ error,
};
});
}
}
-
-
/**
* Pause the movie.
*
@@ -262,8 +254,6 @@ export class MovieStim extends VisualStim
this._movie.pause();
}
-
-
/**
* Stop the movie and reset to 0s.
*
@@ -276,8 +266,6 @@ export class MovieStim extends VisualStim
this.seek(0, log);
}
-
-
/**
* Jump to a specific timepoint
*
@@ -291,9 +279,9 @@ export class MovieStim extends VisualStim
if (timePoint < 0 || timePoint > this._movie.duration)
{
throw {
- origin: 'MovieStim.seek',
+ origin: "MovieStim.seek",
context: `when seeking to timepoint: ${timePoint} of MovieStim: ${this._name}`,
- error: `the timepoint does not belong to [0, ${this._movie.duration}`
+ error: `the timepoint does not belong to [0, ${this._movie.duration}`,
};
}
@@ -310,16 +298,14 @@ export class MovieStim extends VisualStim
catch (error)
{
throw {
- origin: 'MovieStim.seek',
+ origin: "MovieStim.seek",
context: `when seeking to timepoint: ${timePoint} of MovieStim: ${this._name}`,
- error
+ error,
};
}
}
}
-
-
/**
* Estimate the bounding box.
*
@@ -331,21 +317,19 @@ export class MovieStim extends VisualStim
_estimateBoundingBox()
{
const size = this._getDisplaySize();
- if (typeof size !== 'undefined')
+ if (typeof size !== "undefined")
{
this._boundingBox = new PIXI.Rectangle(
this._pos[0] - size[0] / 2,
this._pos[1] - size[1] / 2,
size[0],
- size[1]
+ size[1],
);
}
// TODO take the orientation into account
}
-
-
/**
* Update the stimulus, if necessary.
*
@@ -365,20 +349,20 @@ export class MovieStim extends VisualStim
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
// Leave original video in place
// https://pixijs.download/dev/docs/PIXI.Sprite.html#destroy
this._pixi.destroy({
children: true,
texture: true,
- baseTexture: false
+ baseTexture: false,
});
}
this._pixi = undefined;
// no movie to draw: return immediately
- if (typeof this._movie === 'undefined')
+ if (typeof this._movie === "undefined")
{
return;
}
@@ -416,7 +400,7 @@ export class MovieStim extends VisualStim
this._pixi.scale.y = this.flipVert ? scaleY : -scaleY;
// set the position, rotation, and anchor (movie centered on pos):
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.rotation = this.ori * Math.PI / 180;
this._pixi.anchor.x = 0.5;
this._pixi.anchor.y = 0.5;
@@ -425,8 +409,6 @@ export class MovieStim extends VisualStim
this._estimateBoundingBox();
}
-
-
/**
* Get the size of the display image, which is either that of the ImageStim or that of the image
* it contains.
@@ -439,18 +421,16 @@ export class MovieStim extends VisualStim
{
let displaySize = this.size;
- if (typeof displaySize === 'undefined')
+ if (typeof displaySize === "undefined")
{
// use the size of the texture, if we have access to it:
- if (typeof this._texture !== 'undefined' && this._texture.width > 0)
+ if (typeof this._texture !== "undefined" && this._texture.width > 0)
{
const textureSize = [this._texture.width, this._texture.height];
- displaySize = util.to_unit(textureSize, 'pix', this.win, this.units);
+ displaySize = util.to_unit(textureSize, "pix", this.win, this.units);
}
}
return displaySize;
}
-
-
}
diff --git a/src/visual/Polygon.js b/src/visual/Polygon.js
index d861ffa..7e8196d 100644
--- a/src/visual/Polygon.js
+++ b/src/visual/Polygon.js
@@ -7,10 +7,8 @@
* @license Distributed under the terms of the MIT License
*/
-
-import {ShapeStim} from './ShapeStim';
-import {Color} from '../util/Color';
-
+import { Color } from "../util/Color.js";
+import { ShapeStim } from "./ShapeStim.js";
/**
*
Polygonal visual stimulus.
@@ -39,7 +37,7 @@ import {Color} from '../util/Color';
*/
export class Polygon extends ShapeStim
{
- constructor({name, win, lineWidth, lineColor, fillColor, opacity, edges, radius, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog} = {})
+ constructor({ name, win, lineWidth, lineColor, fillColor, opacity, edges, radius, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
{
super({
name,
@@ -56,20 +54,20 @@ export class Polygon extends ShapeStim
depth,
interpolate,
autoDraw,
- autoLog
+ autoLog,
});
- this._psychoJS.logger.debug('create a new Polygon with name: ', name);
+ this._psychoJS.logger.debug("create a new Polygon with name: ", name);
this._addAttribute(
- 'edges',
+ "edges",
edges,
- 3
+ 3,
);
this._addAttribute(
- 'radius',
+ "radius",
radius,
- 0.5
+ 0.5,
);
this._updateVertices();
@@ -80,8 +78,6 @@ export class Polygon extends ShapeStim
}
}
-
-
/**
* Setter for the radius attribute.
*
@@ -92,7 +88,7 @@ export class Polygon extends ShapeStim
*/
setRadius(radius, log = false)
{
- const hasChanged = this._setAttribute('radius', radius, log);
+ const hasChanged = this._setAttribute("radius", radius, log);
if (hasChanged)
{
@@ -100,8 +96,6 @@ export class Polygon extends ShapeStim
}
}
-
-
/**
* Setter for the edges attribute.
*
@@ -112,7 +106,7 @@ export class Polygon extends ShapeStim
*/
setEdges(edges, log = false)
{
- const hasChanged = this._setAttribute('edges', Math.round(edges), log);
+ const hasChanged = this._setAttribute("edges", Math.round(edges), log);
if (hasChanged)
{
@@ -120,8 +114,6 @@ export class Polygon extends ShapeStim
}
}
-
-
/**
* Update the vertices.
*
@@ -130,7 +122,7 @@ export class Polygon extends ShapeStim
*/
_updateVertices()
{
- this._psychoJS.logger.debug('update the vertices of Polygon: ', this.name);
+ this._psychoJS.logger.debug("update the vertices of Polygon: ", this.name);
const angle = 2.0 * Math.PI / this._edges;
const vertices = [];
@@ -141,5 +133,4 @@ export class Polygon extends ShapeStim
this.setVertices(vertices);
}
-
}
diff --git a/src/visual/Rect.js b/src/visual/Rect.js
index 063897f..e067f84 100644
--- a/src/visual/Rect.js
+++ b/src/visual/Rect.js
@@ -7,10 +7,8 @@
* @license Distributed under the terms of the MIT License
*/
-
-import {ShapeStim} from './ShapeStim';
-import {Color} from '../util/Color';
-
+import { Color } from "../util/Color.js";
+import { ShapeStim } from "./ShapeStim.js";
/**
* Rectangular visual stimulus.
@@ -39,7 +37,7 @@ import {Color} from '../util/Color';
*/
export class Rect extends ShapeStim
{
- constructor({name, win, lineWidth, lineColor, fillColor, opacity, width, height, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog} = {})
+ constructor({ name, win, lineWidth, lineColor, fillColor, opacity, width, height, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
{
super({
name,
@@ -56,20 +54,20 @@ export class Rect extends ShapeStim
depth,
interpolate,
autoDraw,
- autoLog
+ autoLog,
});
- this._psychoJS.logger.debug('create a new Rect with name: ', name);
+ this._psychoJS.logger.debug("create a new Rect with name: ", name);
this._addAttribute(
- 'width',
+ "width",
width,
- 0.5
+ 0.5,
);
this._addAttribute(
- 'height',
+ "height",
height,
- 0.5
+ 0.5,
);
this._updateVertices();
@@ -80,8 +78,6 @@ export class Rect extends ShapeStim
}
}
-
-
/**
* Setter for the width attribute.
*
@@ -92,9 +88,9 @@ export class Rect extends ShapeStim
*/
setWidth(width, log = false)
{
- this._psychoJS.logger.debug('set the width of Rect: ', this.name, 'to: ', width);
+ this._psychoJS.logger.debug("set the width of Rect: ", this.name, "to: ", width);
- const hasChanged = this._setAttribute('width', width, log);
+ const hasChanged = this._setAttribute("width", width, log);
if (hasChanged)
{
@@ -102,8 +98,6 @@ export class Rect extends ShapeStim
}
}
-
-
/**
* Setter for the height attribute.
*
@@ -114,9 +108,9 @@ export class Rect extends ShapeStim
*/
setHeight(height, log = false)
{
- this._psychoJS.logger.debug('set the height of Rect: ', this.name, 'to: ', height);
+ this._psychoJS.logger.debug("set the height of Rect: ", this.name, "to: ", height);
- const hasChanged = this._setAttribute('height', height, log);
+ const hasChanged = this._setAttribute("height", height, log);
if (hasChanged)
{
@@ -124,8 +118,6 @@ export class Rect extends ShapeStim
}
}
-
-
/**
* Update the vertices.
*
@@ -134,7 +126,7 @@ export class Rect extends ShapeStim
*/
_updateVertices()
{
- this._psychoJS.logger.debug('update the vertices of Rect: ', this.name);
+ this._psychoJS.logger.debug("update the vertices of Rect: ", this.name);
const halfWidth = this._width / 2.0;
const halfHeight = this._height / 2.0;
@@ -143,8 +135,7 @@ export class Rect extends ShapeStim
[-halfWidth, -halfHeight],
[halfWidth, -halfHeight],
[halfWidth, halfHeight],
- [-halfWidth, halfHeight]
+ [-halfWidth, halfHeight],
]);
}
-
}
diff --git a/src/visual/ShapeStim.js b/src/visual/ShapeStim.js
index 4594ec0..7baea8b 100644
--- a/src/visual/ShapeStim.js
+++ b/src/visual/ShapeStim.js
@@ -8,14 +8,13 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import * as util from '../util/Util';
-import {WindowMixin} from "../core/WindowMixin";
-
+import * as PIXI from "pixi.js-legacy";
+import { WindowMixin } from "../core/WindowMixin.js";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { VisualStim } from "./VisualStim.js";
/**
* This class provides the basic functionality of shape stimuli.
@@ -44,9 +43,9 @@ import {WindowMixin} from "../core/WindowMixin";
*/
export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
{
- constructor({name, win, lineWidth, lineColor, fillColor, opacity, vertices, closeShape, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog} = {})
+ constructor({ name, win, lineWidth, lineColor, fillColor, opacity, vertices, closeShape, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
{
- super({name, win, units, ori, opacity, pos, depth, size, autoDraw, autoLog});
+ super({ name, win, units, ori, opacity, pos, depth, size, autoDraw, autoLog });
// the PIXI polygon corresponding to the vertices, in pixel units:
this._pixiPolygon_px = undefined;
@@ -54,58 +53,56 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
this._vertices_px = undefined;
// shape:
- if (typeof size === 'undefined' || size === null)
+ if (typeof size === "undefined" || size === null)
{
this.size = [1.0, 1.0];
}
this._addAttribute(
- 'vertices',
+ "vertices",
vertices,
- [[-0.5, 0], [0, 0.5], [0.5, 0]]
+ [[-0.5, 0], [0, 0.5], [0.5, 0]],
);
this._addAttribute(
- 'closeShape',
+ "closeShape",
closeShape,
true,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'interpolate',
+ "interpolate",
interpolate,
true,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'lineWidth',
+ "lineWidth",
lineWidth,
1.5,
- this._onChange(true, true)
+ this._onChange(true, true),
);
// colors:
this._addAttribute(
- 'lineColor',
+ "lineColor",
lineColor,
- 'white',
- this._onChange(true, false)
+ "white",
+ this._onChange(true, false),
);
this._addAttribute(
- 'fillColor',
+ "fillColor",
fillColor,
undefined,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
}
-
-
/**
* Setter for the vertices attribute.
*
@@ -117,16 +114,16 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
setVertices(vertices, log = false)
{
const response = {
- origin: 'ShapeStim.setVertices',
- context: 'when setting the vertices of ShapeStim: ' + this._name
+ origin: "ShapeStim.setVertices",
+ context: "when setting the vertices of ShapeStim: " + this._name,
};
- this._psychoJS.logger.debug('set the vertices of ShapeStim:', this.name);
+ this._psychoJS.logger.debug("set the vertices of ShapeStim:", this.name);
try
{
// if vertices is a string, we check whether it is a known shape:
- if (typeof vertices === 'string')
+ if (typeof vertices === "string")
{
if (vertices in ShapeStim.KnownShapes)
{
@@ -138,18 +135,16 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
}
}
- this._setAttribute('vertices', vertices, log);
+ this._setAttribute("vertices", vertices, log);
this._onChange(true, true)();
}
catch (error)
{
- throw Object.assign(response, {error: error});
+ throw Object.assign(response, { error: error });
}
}
-
-
/**
* Determine whether an object is inside the bounding box of the ShapeStim.
*
@@ -167,24 +162,22 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
// get the position of the object, in pixel coordinates:
const objectPos_px = util.getPositionFromObject(object, units);
- if (typeof objectPos_px === 'undefined')
+ if (typeof objectPos_px === "undefined")
{
throw {
- origin: 'VisualStim.contains',
- context: 'when determining whether VisualStim: ' + this._name + ' contains object: ' + util.toString(object),
- error: 'unable to determine the position of the object'
+ origin: "VisualStim.contains",
+ context: "when determining whether VisualStim: " + this._name + " contains object: " + util.toString(object),
+ error: "unable to determine the position of the object",
};
}
// test for inclusion:
const pos_px = util.to_px(this.pos, this.units, this.win);
this._getVertices_px();
- const polygon_px = this._vertices_px.map(v => [v[0] + pos_px[0], v[1] + pos_px[1]]);
+ const polygon_px = this._vertices_px.map((v) => [v[0] + pos_px[0], v[1] + pos_px[1]]);
return util.IsPointInsidePolygon(objectPos_px, polygon_px);
}
-
-
/**
* Estimate the bounding box.
*
@@ -202,7 +195,7 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
Number.POSITIVE_INFINITY,
Number.POSITIVE_INFINITY,
Number.NEGATIVE_INFINITY,
- Number.NEGATIVE_INFINITY
+ Number.NEGATIVE_INFINITY,
];
for (const vertex of this._vertices_px)
{
@@ -216,14 +209,12 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
this._pos[0] + this._getLengthUnits(limits_px[0]),
this._pos[1] + this._getLengthUnits(limits_px[1]),
this._getLengthUnits(limits_px[2] - limits_px[0]),
- this._getLengthUnits(limits_px[3] - limits_px[1])
+ this._getLengthUnits(limits_px[3] - limits_px[1]),
);
// TODO take the orientation into account
}
-
-
/**
* Update the stimulus, if necessary.
*
@@ -243,7 +234,7 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
@@ -255,25 +246,23 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
// prepare the polygon in the given color and opacity:
this._pixi = new PIXI.Graphics();
this._pixi.lineStyle(this._lineWidth, this._lineColor.int, this._opacity, 0.5);
- if (typeof this._fillColor !== 'undefined' && this._fillColor !== null)
+ if (typeof this._fillColor !== "undefined" && this._fillColor !== null)
{
const contrastedColor = this.getContrastedColor(new Color(this._fillColor), this._contrast);
this._pixi.beginFill(contrastedColor.int, this._opacity);
}
this._pixi.drawPolygon(this._pixiPolygon_px);
- if (typeof this._fillColor !== 'undefined' && this._fillColor !== null)
+ if (typeof this._fillColor !== "undefined" && this._fillColor !== null)
{
this._pixi.endFill();
}
}
// set polygon position and rotation:
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.rotation = this.ori * Math.PI / 180.0;
}
-
-
/**
* Get the PIXI polygon (in pixel units) corresponding to the vertices.
*
@@ -311,8 +300,6 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
return this._pixiPolygon_px;
}
-
-
/**
* Get the vertices in pixel units.
*
@@ -324,28 +311,28 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
{
// handle flipping:
let flip = [1.0, 1.0];
- if ('_flipHoriz' in this && this._flipHoriz)
+ if ("_flipHoriz" in this && this._flipHoriz)
{
flip[0] = -1.0;
}
- if ('_flipVert' in this && this._flipVert)
+ if ("_flipVert" in this && this._flipVert)
{
flip[1] = -1.0;
}
// handle size, flipping, and convert to pixel units:
- this._vertices_px = this._vertices.map(v => util.to_px(
- [v[0] * this._size[0] * flip[0], v[1] * this._size[1] * flip[1]],
- this._units,
- this._win)
+ this._vertices_px = this._vertices.map((v) =>
+ util.to_px(
+ [v[0] * this._size[0] * flip[0], v[1] * this._size[1] * flip[1]],
+ this._units,
+ this._win,
+ )
);
return this._vertices_px;
}
-
}
-
/**
* Known shapes.
*
@@ -357,15 +344,15 @@ ShapeStim.KnownShapes = {
[-0.1, +0.5], // up
[+0.1, +0.5],
[+0.1, +0.1],
- [+0.5, +0.1], // right
+ [+0.5, +0.1], // right
[+0.5, -0.1],
[+0.1, -0.1],
- [+0.1, -0.5], // down
+ [+0.1, -0.5], // down
[-0.1, -0.5],
[-0.1, -0.1],
- [-0.5, -0.1], // left
+ [-0.5, -0.1], // left
[-0.5, +0.1],
- [-0.1, +0.1]
+ [-0.1, +0.1],
],
star7: [
@@ -382,7 +369,6 @@ ShapeStim.KnownShapes = {
[-0.49, -0.11],
[-0.19, 0.04],
[-0.39, 0.31],
- [-0.09, 0.18]
- ]
-
+ [-0.09, 0.18],
+ ],
};
diff --git a/src/visual/Slider.js b/src/visual/Slider.js
index d0979a5..8d6985f 100644
--- a/src/visual/Slider.js
+++ b/src/visual/Slider.js
@@ -7,16 +7,15 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import {WindowMixin} from '../core/WindowMixin';
-import {Clock} from '../util/Clock';
-import * as util from '../util/Util';
-import {PsychoJS} from "../core/PsychoJS";
-
+import * as PIXI from "pixi.js-legacy";
+import { PsychoJS } from "../core/PsychoJS.js";
+import { WindowMixin } from "../core/WindowMixin.js";
+import { Clock } from "../util/Clock.js";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { VisualStim } from "./VisualStim.js";
/**
* Slider stimulus.
@@ -69,9 +68,38 @@ import {PsychoJS} from "../core/PsychoJS";
*/
export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
{
- constructor({name, win, pos, size, ori, units, color, markerColor, lineColor, contrast, opacity, style, ticks, labels, granularity, flip, readOnly, font, bold, italic, fontSize, compact, clipMask, autoDraw, autoLog, dependentStims} = {})
+ constructor(
+ {
+ name,
+ win,
+ pos,
+ size,
+ ori,
+ units,
+ color,
+ markerColor,
+ lineColor,
+ contrast,
+ opacity,
+ style,
+ ticks,
+ labels,
+ granularity,
+ flip,
+ readOnly,
+ font,
+ bold,
+ italic,
+ fontSize,
+ compact,
+ clipMask,
+ autoDraw,
+ autoLog,
+ dependentStims,
+ } = {},
+ )
{
- super({name, win, units, ori, opacity, pos, size, clipMask, autoDraw, autoLog});
+ super({ name, win, units, ori, opacity, pos, size, clipMask, autoDraw, autoLog });
this._needMarkerUpdate = false;
@@ -95,119 +123,117 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
};
this._addAttribute(
- 'style',
+ "style",
style,
[Slider.Style.RATING],
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'ticks',
+ "ticks",
ticks,
[1, 2, 3, 4, 5],
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'labels',
+ "labels",
labels,
[],
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'granularity',
+ "granularity",
granularity,
0,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'readOnly',
+ "readOnly",
readOnly,
- false
+ false,
);
this._addAttribute(
- 'compact',
+ "compact",
compact,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
// font:
this._addAttribute(
- 'font',
+ "font",
font,
- 'Arial',
- this._onChange(true, true)
+ "Arial",
+ this._onChange(true, true),
);
this._addAttribute(
- 'fontSize',
+ "fontSize",
fontSize,
- (this._units === 'pix') ? 14 : 0.03,
- this._onChange(true, true)
+ (this._units === "pix") ? 14 : 0.03,
+ this._onChange(true, true),
);
this._addAttribute(
- 'bold',
+ "bold",
bold,
true,
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'italic',
+ "italic",
italic,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'flip',
+ "flip",
flip,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
// color:
this._addAttribute(
- 'color',
+ "color",
color,
- 'lightgray',
- this._onChange(true, false)
+ "lightgray",
+ this._onChange(true, false),
);
this._addAttribute(
- 'lineColor',
+ "lineColor",
lineColor,
- 'lightgray',
- this._onChange(true, false)
+ "lightgray",
+ this._onChange(true, false),
);
this._addAttribute(
- 'markerColor',
+ "markerColor",
markerColor,
- 'red',
- this._onChange(true, false)
+ "red",
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'dependentStims',
+ "dependentStims",
dependentStims,
[],
- this._onChange(false, false)
+ this._onChange(false, false),
);
-
-
// slider rating (which might be different from the visible marker rating):
- this._addAttribute('rating', undefined);
+ this._addAttribute("rating", undefined);
// visible marker rating (which might be different from the actual rating):
- this._addAttribute('markerPos', undefined);
+ this._addAttribute("markerPos", undefined);
// full history of ratings and response times:
- this._addAttribute('history', []);
+ this._addAttribute("history", []);
// various graphical components:
- this._addAttribute('lineAspectRatio', 0.01);
+ this._addAttribute("lineAspectRatio", 0.01);
// check for attribute conflicts, missing values, etc.:
this._sanitizeAttributes();
@@ -224,8 +250,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Force a refresh of the stimulus.
*
@@ -239,8 +263,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._needMarkerUpdate = true;
}
-
-
/**
* Reset the slider.
*
@@ -249,7 +271,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
*/
reset()
{
- this.psychoJS.logger.debug('reset Slider: ', this._name);
+ this.psychoJS.logger.debug("reset Slider: ", this._name);
this._markerPos = undefined;
this._history = [];
@@ -261,14 +283,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._needUpdate = true;
// the marker should be invisible when markerPos is undefined:
- if (typeof this._marker !== 'undefined')
+ if (typeof this._marker !== "undefined")
{
this._marker.alpha = 0;
}
}
-
-
/**
* Get the current value of the rating.
*
@@ -289,8 +309,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Get the response time of the most recent change to the rating.
*
@@ -311,8 +329,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Setter for the readOnly attribute.
*
@@ -326,7 +342,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
*/
setReadOnly(readOnly = true, log = false)
{
- const hasChanged = this._setAttribute('readOnly', readOnly, log);
+ const hasChanged = this._setAttribute("readOnly", readOnly, log);
if (hasChanged)
{
@@ -344,8 +360,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Setter for the markerPos attribute.
*
@@ -371,8 +385,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Setter for the rating attribute.
*
@@ -392,60 +404,51 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
rating = this._labels[Math.round(rating)];
}
- this._setAttribute('rating', rating, log);
+ this._setAttribute("rating", rating, log);
}
-
-
/** Let `borderColor` alias `lineColor` to parallel PsychoPy */
- set borderColor(color) {
+ set borderColor(color)
+ {
this.lineColor = color;
}
-
-
- setBorderColor(color) {
+ setBorderColor(color)
+ {
this.setLineColor(color);
}
-
-
- get borderColor() {
+ get borderColor()
+ {
return this.lineColor;
}
-
-
- getBorderColor() {
+ getBorderColor()
+ {
return this.getLineColor();
}
-
/** Let `fillColor` alias `markerColor` to parallel PsychoPy */
- set fillColor(color) {
+ set fillColor(color)
+ {
this.markerColor = color;
}
-
-
- setFillColor(color) {
+ setFillColor(color)
+ {
this.setMarkerColor(color);
}
-
-
- get fillColor() {
+ get fillColor()
+ {
return this.markerColor;
}
-
-
- getFillColor() {
+ getFillColor()
+ {
return this.getMarkerColor();
}
-
-
/**
* Estimate the bounding box.
*
@@ -461,18 +464,18 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
{
// setup the slider's style (taking into account the Window dimension, etc.):
this._setupStyle();
-
+
// calculate various values in pixel units:
this._tickSize_px = util.to_px(this._tickSize, this._units, this._win);
this._fontSize_px = this._getLengthPix(this._fontSize);
- this._barSize_px = util.to_px(this._barSize, this._units, this._win, true).map(v => Math.max(1, v));
+ this._barSize_px = util.to_px(this._barSize, this._units, this._win, true).map((v) => Math.max(1, v));
this._markerSize_px = util.to_px(this._markerSize, this._units, this._win, true);
const pos_px = util.to_px(this._pos, this._units, this._win);
const size_px = util.to_px(this._size, this._units, this._win);
// calculate the position of the ticks:
const tickPositions = this._ratingToPos(this._ticks);
- this._tickPositions_px = tickPositions.map(p => util.to_px(p, this._units, this._win));
+ this._tickPositions_px = tickPositions.map((p) => util.to_px(p, this._units, this._win));
// left, top, right, bottom limits:
const limits_px = [0, 0, size_px[0], size_px[1]];
@@ -489,7 +492,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
for (let l = 0; l < this._labels.length; ++l)
{
- const tickPositionIndex = Math.round( l / (this._labels.length - 1) * (this._ticks.length - 1) );
+ const tickPositionIndex = Math.round(l / (this._labels.length - 1) * (this._ticks.length - 1));
this._labelPositions_px[l] = this._tickPositions_px[tickPositionIndex];
const labelBounds = PIXI.TextMetrics.measureText(this._labels[l].toString(), labelTextStyle);
@@ -514,8 +517,10 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
// ensure that that labels are not overlapping:
- if (prevLabelBounds &&
- (this._labelPositions_px[l - 1][0] + prevLabelBounds.width + tolerance >= this._labelPositions_px[l][0]))
+ if (
+ prevLabelBounds
+ && (this._labelPositions_px[l - 1][0] + prevLabelBounds.width + tolerance >= this._labelPositions_px[l][0])
+ )
{
if (prevNonOverlapOffset === 0)
{
@@ -575,12 +580,10 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._getLengthUnits(position_px.x + limits_px[0]),
this._getLengthUnits(position_px.y + limits_px[1]),
this._getLengthUnits(limits_px[2] - limits_px[0]),
- this._getLengthUnits(limits_px[3] - limits_px[1])
+ this._getLengthUnits(limits_px[3] - limits_px[1]),
);
}
-
-
/**
* Sanitize the slider attributes: check for attribute conflicts, missing values, etc.
*
@@ -591,9 +594,9 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
_sanitizeAttributes()
{
// convert potential string styles into Symbols:
- this._style.forEach( (style, index) =>
+ this._style.forEach((style, index) =>
{
- if (typeof style === 'string')
+ if (typeof style === "string")
{
this._style[index] = Symbol.for(style.toUpperCase());
}
@@ -605,14 +608,11 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._isCategorical = (this._ticks.length === 0);
if (this._isCategorical)
{
- this._ticks = [...Array(this._labels.length)].map( (_, i) => i );
+ this._ticks = [...Array(this._labels.length)].map((_, i) => i);
this._granularity = 1.0;
}
-
}
-
-
/**
* Set the current rating.
*
@@ -628,7 +628,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
recordRating(rating, responseTime = undefined, log = false)
{
// get response time:
- if (typeof responseTime === 'undefined')
+ if (typeof responseTime === "undefined")
{
responseTime = this._responseClock.getTime();
}
@@ -639,15 +639,14 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this.setRating(rating, log);
// add rating and response time to history:
- this._history.push({rating: this._rating, responseTime});
- this.psychoJS.logger.debug('record a new rating: ', this._rating, 'with response time: ', responseTime, 'for Slider: ', this._name);
+ this._history.push({ rating: this._rating, responseTime });
+ this.psychoJS.logger.debug("record a new rating: ", this._rating, "with response time: ", responseTime, "for Slider: ", this._name);
// update slider:
this._needMarkerUpdate = true;
this._needUpdate = true;
}
-
/**
* Update the stimulus, if necessary.
*
@@ -681,7 +680,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
/**
* Estimate the position of the slider, taking the compactness into account.
*
@@ -691,9 +689,11 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
*/
_getPosition_px()
{
- const position = util.to_pixiPoint(this.pos, this.units, this.win, true);
- if (this._compact &&
- (this._style.indexOf(Slider.Style.RADIO) > -1 || this._style.indexOf(Slider.Style.RATING) > -1))
+ const position = to_pixiPoint(this.pos, this.units, this.win, true);
+ if (
+ this._compact
+ && (this._style.indexOf(Slider.Style.RADIO) > -1 || this._style.indexOf(Slider.Style.RATING) > -1)
+ )
{
if (this._isHorizontal())
{
@@ -708,8 +708,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
return position;
}
-
-
/**
* Update the position of the marker if necessary.
*
@@ -724,12 +722,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
this._needMarkerUpdate = false;
- if (typeof this._marker !== 'undefined')
+ if (typeof this._marker !== "undefined")
{
- if (typeof this._markerPos !== 'undefined')
+ if (typeof this._markerPos !== "undefined")
{
const visibleMarkerPos = this._ratingToPos([this._markerPos]);
- this._marker.position = util.to_pixiPoint(visibleMarkerPos[0], this.units, this.win, true);
+ this._marker.position = to_pixiPoint(visibleMarkerPos[0], this.units, this.win, true);
this._marker.alpha = 1;
}
else
@@ -739,8 +737,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Setup the PIXI components of the slider (bar, ticks, labels, marker, etc.).
*
@@ -758,17 +754,15 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._setupStyle();
-
// calculate various values in pixel units:
this._tickSize_px = util.to_px(this._tickSize, this._units, this._win);
this._fontSize_px = this._getLengthPix(this._fontSize);
- this._barSize_px = util.to_px(this._barSize, this._units, this._win, true).map(v => Math.max(1, v));
+ this._barSize_px = util.to_px(this._barSize, this._units, this._win, true).map((v) => Math.max(1, v));
this._markerSize_px = util.to_px(this._markerSize, this._units, this._win, true);
const tickPositions = this._ratingToPos(this._ticks);
- this._tickPositions_px = tickPositions.map(p => util.to_px(p, this._units, this._win));
+ this._tickPositions_px = tickPositions.map((p) => util.to_px(p, this._units, this._win));
-
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
@@ -781,7 +775,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._body.interactive = true;
this._pixi.addChild(this._body);
-
// ensure that pointer events will be captured along the slider body, even outside of
// marker and labels:
if (this._tickType === Slider.Shape.DISC)
@@ -791,7 +784,8 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
-this._barSize_px[0] / 2 - maxTickSize_px,
-this._barSize_px[1] / 2 - maxTickSize_px,
this._barSize_px[0] + maxTickSize_px * 2,
- this._barSize_px[1] + maxTickSize_px * 2);
+ this._barSize_px[1] + maxTickSize_px * 2,
+ );
}
else
{
@@ -799,7 +793,8 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
-this._barSize_px[0] / 2 - this._tickSize_px[0] / 2,
-this._barSize_px[1] / 2 - this._tickSize_px[1] / 2,
this._barSize_px[0] + this._tickSize_px[0],
- this._barSize_px[1] + this._tickSize_px[1]);
+ this._barSize_px[1] + this._tickSize_px[1],
+ );
}
// central bar:
@@ -815,8 +810,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._setupMarker();
}
-
-
/**
* Setup the central bar.
*
@@ -829,7 +822,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
if (this._barLineWidth_px > 0)
{
this._body.lineStyle(this._barLineWidth_px, this._barLineColor.int, 1, 0.5);
- if (typeof this._barFillColor !== 'undefined')
+ if (typeof this._barFillColor !== "undefined")
{
this._body.beginFill(this._barFillColor.int, 1);
}
@@ -837,17 +830,15 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
Math.round(-this._barSize_px[0] / 2),
Math.round(-this._barSize_px[1] / 2),
Math.round(this._barSize_px[0]),
- Math.round(this._barSize_px[1])
+ Math.round(this._barSize_px[1]),
);
- if (typeof this._barFillColor !== 'undefined')
+ if (typeof this._barFillColor !== "undefined")
{
this._body.endFill();
}
}
}
-
-
/**
* Setup the marker, and the associated mouse events.
*
@@ -857,7 +848,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
*/
_setupMarker()
{
-/* this is now deprecated and replaced by _body.hitArea
+ /* this is now deprecated and replaced by _body.hitArea
// transparent rectangle necessary to capture pointer events outside of marker and labels:
const eventCaptureRectangle = new PIXI.Graphics();
eventCaptureRectangle.beginFill(0, 0);
@@ -869,7 +860,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
);
eventCaptureRectangle.endFill();
this._pixi.addChild(eventCaptureRectangle);
-*/
+ */
// marker:
this._marker = new PIXI.Graphics();
@@ -926,7 +917,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
Math.round(-this._markerSize_px[0] / 2),
Math.round(-this._markerSize_px[1] / 2),
this._markerSize_px[0],
- this._markerSize_px[1]
+ this._markerSize_px[1],
);
this._marker.endFill();
@@ -934,7 +925,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
// this._marker.drawCircle(0, 0, this._markerSize_px[0] / 3);
}
-
// marker mouse events:
const self = this;
self._markerDragging = false;
@@ -1000,7 +990,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
};
-
// (*) slider mouse events outside of marker
// note: this only works thanks to eventCaptureRectangle
/* not quite right just yet (as of May 2020)
@@ -1034,8 +1023,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
};
}
-
-
/**
* Setup the ticks.
*
@@ -1071,8 +1058,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Get the PIXI Text Style applied to the PIXI.Text labels.
*
@@ -1083,19 +1068,17 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
_getTextStyle()
{
this._fontSize_px = this._getLengthPix(this._fontSize);
-
+
return new PIXI.TextStyle({
fontFamily: this._font,
fontSize: Math.round(this._fontSize_px),
- fontWeight: (this._bold) ? 'bold' : 'normal',
- fontStyle: (this._italic) ? 'italic' : 'normal',
+ fontWeight: (this._bold) ? "bold" : "normal",
+ fontStyle: (this._italic) ? "italic" : "normal",
fill: this.getContrastedColor(this._labelColor, this._contrast).hex,
- align: 'center',
+ align: "center",
});
}
-
-
/**
* Setup the labels.
*
@@ -1120,8 +1103,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Apply a particular style to the slider.
*
@@ -1157,7 +1138,8 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._tickType = Slider.Shape.LINE;
this._tickColor = (!skin.TICK_COLOR) ? new Color(this._lineColor) : skin.TICK_COLOR;
- if (this.markerColor === undefined) {
+ if (this.markerColor === undefined)
+ {
this.markerColor = skin.MARKER_COLOR;
}
@@ -1170,7 +1152,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._labelOri = 0;
-
// rating:
if (this._style.indexOf(Slider.Style.RATING) > -1)
{
@@ -1183,8 +1164,8 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._markerType = Slider.Shape.TRIANGLE;
if (!this._skin.MARKER_SIZE)
{
- this._markerSize = this._markerSize.map(s => s * 2);
- }
+ this._markerSize = this._markerSize.map((s) => s * 2);
+ }
}
// slider:
@@ -1193,9 +1174,9 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._markerType = Slider.Shape.BOX;
if (!this._skin.MARKER_SIZE)
{
- this._markerSize = (this._isHorizontal()) ?
- [this._size[0] / (this._ticks[this._ticks.length - 1] - this._ticks[0]), this._size[1]] :
- [this._size[0], this._size[1] / (this._ticks[this._ticks.length - 1] - this._ticks[0])];
+ this._markerSize = (this._isHorizontal())
+ ? [this._size[0] / (this._ticks[this._ticks.length - 1] - this._ticks[0]), this._size[1]]
+ : [this._size[0], this._size[1] / (this._ticks[this._ticks.length - 1] - this._ticks[0])];
}
this._barSize = [this._size[0], this._size[1]];
this._barFillColor = this.getContrastedColor(new Color(this.color), 0.5);
@@ -1234,12 +1215,10 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
if (!this._skin.MARKER_SIZE)
{
- this._markerSize = this._markerSize.map(s => s * 0.7);
+ this._markerSize = this._markerSize.map((s) => s * 0.7);
+ }
}
}
- }
-
-
/**
* Convert an array of ratings into an array of [x,y] positions (in Slider units, with 0 at the center of the Slider)
@@ -1259,20 +1238,22 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
// in compact mode the circular markers of RADIO sliders must fit within the width:
if (this._compact && this._style.indexOf(Slider.Style.RADIO) > -1)
{
- return ratings.map(v => [
- ((v - this._ticks[0]) / range) * (this._size[0] - this._tickSize[1]*2) -
- (this._size[0] / 2) + this._tickSize[1],
- 0]);
+ return ratings.map((v) => [
+ ((v - this._ticks[0]) / range) * (this._size[0] - this._tickSize[1] * 2)
+ - (this._size[0] / 2) + this._tickSize[1],
+ 0,
+ ]);
}
else if (this._style.indexOf(Slider.Style.SLIDER) > -1)
{
- return ratings.map(v => [
+ return ratings.map((v) => [
((v - this._ticks[0]) / range - 0.5) * (this._size[0] - this._markerSize[0]),
- 0]);
+ 0,
+ ]);
}
else
{
- return ratings.map(v => [((v - this._ticks[0]) / range - 0.5) * this._size[0], 0]);
+ return ratings.map((v) => [((v - this._ticks[0]) / range - 0.5) * this._size[0], 0]);
}
}
else
@@ -1280,25 +1261,26 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
// in compact mode the circular markers of RADIO sliders must fit within the height:
if (this._compact && this._style.indexOf(Slider.Style.RADIO) > -1)
{
- return ratings.map(v => [0,
- ((v - this._ticks[0]) / range) * (this._size[1] - this._tickSize[0]*2) -
- (this._size[1] / 2) + this._tickSize[0]]);
+ return ratings.map((v) => [
+ 0,
+ ((v - this._ticks[0]) / range) * (this._size[1] - this._tickSize[0] * 2)
+ - (this._size[1] / 2) + this._tickSize[0],
+ ]);
}
else if (this._style.indexOf(Slider.Style.SLIDER) > -1)
{
- return ratings.map(v => [
+ return ratings.map((v) => [
0,
- ((v - this._ticks[0]) / range - 0.5) * (this._size[1] - this._markerSize[1])]);
+ ((v - this._ticks[0]) / range - 0.5) * (this._size[1] - this._markerSize[1]),
+ ]);
}
else
{
- return ratings.map(v => [0, (1.0 - (v - this._ticks[0]) / range - 0.5) * this._size[1]]);
+ return ratings.map((v) => [0, (1.0 - (v - this._ticks[0]) / range - 0.5) * this._size[1]]);
}
}
}
-
-
/**
* Convert a [x,y] position, in pixel units, relative to the slider, into a rating.
*
@@ -1338,8 +1320,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
}
}
-
-
/**
* Determine whether the slider is horizontal.
*
@@ -1355,8 +1335,6 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
return (this._size[0] > this._size[1]);
}
-
-
/**
* Calculate the rating once granularity has been taken into account.
*
@@ -1368,7 +1346,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
*/
_granularise(rating)
{
- if (typeof rating === 'undefined')
+ if (typeof rating === "undefined")
{
return undefined;
}
@@ -1381,10 +1359,8 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
return rating;
}
-
}
-
/**
* Shape of the marker and of the ticks.
*
@@ -1394,13 +1370,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
* @public
*/
Slider.Shape = {
- DISC: Symbol.for('DISC'),
- TRIANGLE: Symbol.for('TRIANGLE'),
- LINE: Symbol.for('LINE'),
- BOX: Symbol.for('BOX')
+ DISC: Symbol.for("DISC"),
+ TRIANGLE: Symbol.for("TRIANGLE"),
+ LINE: Symbol.for("LINE"),
+ BOX: Symbol.for("BOX"),
};
-
/**
* Styles.
*
@@ -1410,15 +1385,14 @@ Slider.Shape = {
* @public
*/
Slider.Style = {
- RATING: Symbol.for('RATING'),
- TRIANGLE_MARKER: Symbol.for('TRIANGLE_MARKER'),
- SLIDER: Symbol.for('SLIDER'),
- WHITE_ON_BLACK: Symbol.for('WHITE_ON_BLACK'),
- LABELS_45: Symbol.for('LABELS_45'),
- RADIO: Symbol.for('RADIO')
+ RATING: Symbol.for("RATING"),
+ TRIANGLE_MARKER: Symbol.for("TRIANGLE_MARKER"),
+ SLIDER: Symbol.for("SLIDER"),
+ WHITE_ON_BLACK: Symbol.for("WHITE_ON_BLACK"),
+ LABELS_45: Symbol.for("LABELS_45"),
+ RADIO: Symbol.for("RADIO"),
};
-
/**
* Skin.
*
@@ -1432,15 +1406,15 @@ Slider.Style = {
Slider.Skin = {
MARKER_SIZE: null,
STANDARD: {
- MARKER_COLOR: new Color('red'),
+ MARKER_COLOR: new Color("red"),
BAR_LINE_COLOR: null,
TICK_COLOR: null,
- LABEL_COLOR: null
+ LABEL_COLOR: null,
},
WHITE_ON_BLACK: {
- MARKER_COLOR: new Color('white'),
- BAR_LINE_COLOR: new Color('black'),
- TICK_COLOR: new Color('black'),
- LABEL_COLOR: new Color('black')
- }
+ MARKER_COLOR: new Color("white"),
+ BAR_LINE_COLOR: new Color("black"),
+ TICK_COLOR: new Color("black"),
+ LABEL_COLOR: new Color("black"),
+ },
};
diff --git a/src/visual/TextBox.js b/src/visual/TextBox.js
index 01b2b7e..af42324 100644
--- a/src/visual/TextBox.js
+++ b/src/visual/TextBox.js
@@ -7,14 +7,13 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import {TextInput} from './TextInput';
-import {ButtonStim} from './ButtonStim.js';
-import * as util from '../util/Util';
+import * as PIXI from "pixi.js-legacy";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import * as util from "../util/Util.js";
+import { ButtonStim } from "./ButtonStim.js";
+import { TextInput } from "./TextInput.js";
+import { VisualStim } from "./VisualStim.js";
// TODO finish documenting all options
/**
@@ -50,122 +49,154 @@ import * as util from '../util/Util';
*/
export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{
- constructor({name, win, pos, anchor, size, units, ori, opacity, depth, text, font, letterHeight, bold, italic, alignment, color, contrast, flipHoriz, flipVert, fillColor, borderColor, borderWidth, padding, editable, multiline, autofocus, clipMask, autoDraw, autoLog} = {})
+ constructor(
+ {
+ name,
+ win,
+ pos,
+ anchor,
+ size,
+ units,
+ ori,
+ opacity,
+ depth,
+ text,
+ font,
+ letterHeight,
+ bold,
+ italic,
+ alignment,
+ color,
+ contrast,
+ flipHoriz,
+ flipVert,
+ fillColor,
+ borderColor,
+ borderWidth,
+ padding,
+ editable,
+ multiline,
+ autofocus,
+ clipMask,
+ autoDraw,
+ autoLog,
+ } = {},
+ )
{
- super({name, win, pos, size, units, ori, opacity, depth, clipMask, autoDraw, autoLog});
+ super({ name, win, pos, size, units, ori, opacity, depth, clipMask, autoDraw, autoLog });
this._addAttribute(
- 'text',
+ "text",
text,
- '',
- this._onChange(true, true)
+ "",
+ this._onChange(true, true),
);
this._addAttribute(
- 'placeholder',
+ "placeholder",
text,
- '',
- this._onChange(true, true)
- );
+ "",
+ this._onChange(true, true),
+ );
this._addAttribute(
- 'anchor',
+ "anchor",
anchor,
- 'center',
- this._onChange(false, true)
+ "center",
+ this._onChange(false, true),
);
this._addAttribute(
- 'flipHoriz',
+ "flipHoriz",
flipHoriz,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'flipVert',
+ "flipVert",
flipVert,
false,
- this._onChange(false, false)
+ this._onChange(false, false),
);
// font:
this._addAttribute(
- 'font',
+ "font",
font,
- 'Arial',
- this._onChange(true, true)
+ "Arial",
+ this._onChange(true, true),
);
this._addAttribute(
- 'letterHeight',
+ "letterHeight",
letterHeight,
this._getDefaultLetterHeight(),
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'bold',
+ "bold",
bold,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'italic',
+ "italic",
italic,
false,
- this._onChange(true, true)
+ this._onChange(true, true),
);
this._addAttribute(
- 'alignment',
+ "alignment",
alignment,
- 'left',
- this._onChange(true, true)
+ "left",
+ this._onChange(true, true),
);
// colors:
this._addAttribute(
- 'color',
+ "color",
color,
- 'white',
- this._onChange(true, false)
+ "white",
+ this._onChange(true, false),
);
this._addAttribute(
- 'fillColor',
+ "fillColor",
fillColor,
- 'lightgrey',
- this._onChange(true, false)
+ "lightgrey",
+ this._onChange(true, false),
);
this._addAttribute(
- 'borderColor',
+ "borderColor",
borderColor,
this.fillColor,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
// default border width: 1px
this._addAttribute(
- 'borderWidth',
+ "borderWidth",
borderWidth,
- util.to_unit([1, 0], 'pix', win, this._units)[0],
- this._onChange(true, true)
+ util.to_unit([1, 0], "pix", win, this._units)[0],
+ this._onChange(true, true),
);
// default padding: half of the letter height
this._addAttribute(
- 'padding',
+ "padding",
padding,
this._letterHeight / 2.0,
- this._onChange(true, true)
+ this._onChange(true, true),
);
- this._addAttribute('multiline', multiline, false, this._onChange(true, true));
- this._addAttribute('editable', editable, false, this._onChange(true, true));
- this._addAttribute('autofocus', autofocus, true, this._onChange(true, false));
- // this._setAttribute({
- // name: 'vertices',
- // value: vertices,
- // assert: v => (v != null) && (typeof v !== 'undefined') && Array.isArray(v) )
- // log);
+ this._addAttribute("multiline", multiline, false, this._onChange(true, true));
+ this._addAttribute("editable", editable, false, this._onChange(true, true));
+ this._addAttribute("autofocus", autofocus, true, this._onChange(true, false));
+ // this._setAttribute({
+ // name: 'vertices',
+ // value: vertices,
+ // assert: v => (v != null) && (typeof v !== 'undefined') && Array.isArray(v) )
+ // log);
// estimate the bounding box:
this._estimateBoundingBox();
@@ -176,7 +207,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
}
}
-
/**
* Clears the current text value or sets it back to match the placeholder.
*
@@ -185,13 +215,11 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
*/
reset()
{
- const text = this.editable ? '' : this.placeholder;
+ const text = this.editable ? "" : this.placeholder;
this.setText(this.placeholder);
}
-
-
/**
* Clears the current text value.
*
@@ -203,8 +231,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this.setText();
}
-
-
/**
* For tweaking the underlying input value.
*
@@ -212,9 +238,9 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
* @public
* @param {string} text
*/
- setText(text = '')
+ setText(text = "")
{
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.text = text;
}
@@ -222,7 +248,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this._text = text;
}
-
/**
* For accessing the underlying input value.
*
@@ -232,7 +257,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
*/
getText()
{
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
return this._pixi.text;
}
@@ -240,7 +265,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
return this._text;
}
-
/**
* Setter for the size attribute.
*
@@ -253,25 +277,25 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{
// test with the size is undefined, or [undefined, undefined]:
let isSizeUndefined = (
- (typeof size === 'undefined') || (size === null) ||
- ( Array.isArray(size) && size.every( v => typeof v === 'undefined' || v === null) )
- );
+ (typeof size === "undefined") || (size === null)
+ || (Array.isArray(size) && size.every((v) => typeof v === "undefined" || v === null))
+ );
if (isSizeUndefined)
{
size = TextBox._defaultSizeMap.get(this._units);
- if (typeof size === 'undefined')
+ if (typeof size === "undefined")
{
throw {
- origin: 'TextBox.setSize',
- context: 'when setting the size of TextBox: ' + this._name,
- error: 'no default size for unit: ' + this._units
+ origin: "TextBox.setSize",
+ context: "when setting the size of TextBox: " + this._name,
+ error: "no default size for unit: " + this._units,
};
}
}
- const hasChanged = this._setAttribute('size', size, log);
+ const hasChanged = this._setAttribute("size", size, log);
if (hasChanged)
{
@@ -283,8 +307,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Get the default letter height given the stimulus' units.
*
@@ -296,20 +318,18 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{
const height = TextBox._defaultLetterHeightMap.get(this._units);
- if (typeof height === 'undefined')
+ if (typeof height === "undefined")
{
throw {
- origin: 'TextBox._getDefaultLetterHeight',
- context: 'when getting the default height of TextBox: ' + this._name,
- error: 'no default letter height for unit: ' + this._units
+ origin: "TextBox._getDefaultLetterHeight",
+ context: "when getting the default height of TextBox: " + this._name,
+ error: "no default letter height for unit: " + this._units,
};
}
return height;
}
-
-
/**
* Get the TextInput options applied to the PIXI.TextInput.
*
@@ -328,24 +348,24 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
return {
input: {
fontFamily: this._font,
- fontSize: letterHeight_px + 'px',
+ fontSize: letterHeight_px + "px",
color: new Color(this._color).hex,
- fontWeight: (this._bold) ? 'bold' : 'normal',
- fontStyle: (this._italic) ? 'italic' : 'normal',
+ fontWeight: (this._bold) ? "bold" : "normal",
+ fontStyle: (this._italic) ? "italic" : "normal",
- padding: padding_px + 'px',
+ padding: padding_px + "px",
multiline,
text: this._text,
- height: multiline ? (height_px - 2 * padding_px) + 'px' : undefined,
- width: (width_px - 2 * padding_px) + 'px'
+ height: multiline ? (height_px - 2 * padding_px) + "px" : undefined,
+ width: (width_px - 2 * padding_px) + "px",
},
box: {
fill: new Color(this._fillColor).int,
rounded: 5,
stroke: {
color: new Color(this._borderColor).int,
- width: borderWidth_px
- }
+ width: borderWidth_px,
+ },
/*default: {
fill: new Color(this._fillColor).int,
rounded: 5,
@@ -370,12 +390,10 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
width: borderWidth_px
}
}*/
- }
+ },
};
}
-
-
/**
* Estimate the bounding box.
*
@@ -395,14 +413,12 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this._pos[0] - anchor[0] * this._size[0],
this._pos[1] - anchor[1] * boxHeight,
this._size[0],
- boxHeight
+ boxHeight,
);
// TODO take the orientation into account
}
-
-
/**
* Update the stimulus, if necessary.
*
@@ -424,13 +440,13 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
// Get the currently entered text
- let enteredText = this._pixi !== undefined? this._pixi.text: '';
- // Create new TextInput
+ let enteredText = this._pixi !== undefined ? this._pixi.text : "";
+ // Create new TextInput
this._pixi = new TextInput(this._getTextInputOptions());
// listeners required for regular textboxes, but may cause problems with button stimuli
@@ -441,7 +457,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
// check if other TextBox instances are already in focus
const { _drawList = [] } = this.psychoJS.window;
- const otherTextBoxWithFocus = _drawList.some(item => item instanceof TextBox && item._pixi && item._pixi._hasFocus());
+ const otherTextBoxWithFocus = _drawList.some((item) => item instanceof TextBox && item._pixi && item._pixi._hasFocus());
if (this._autofocus && !otherTextBoxWithFocus)
{
this._pixi._onSurrogateFocus();
@@ -452,7 +468,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
}
if (this._editable)
{
- this.text = enteredText;
+ this.text = enteredText;
this._pixi.placeholder = this._placeholder;
}
else
@@ -479,8 +495,6 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this._pixi.mask = this._clipMask;
}
-
-
/**
* Convert the anchor attribute into numerical values.
*
@@ -493,30 +507,27 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{
const anchor = [0.5, 0.5];
- if (this._anchor.indexOf('left') > -1)
+ if (this._anchor.indexOf("left") > -1)
{
anchor[0] = 0;
}
- else if (this._anchor.indexOf('right') > -1)
+ else if (this._anchor.indexOf("right") > -1)
{
anchor[0] = 1;
}
- if (this._anchor.indexOf('top') > -1)
+ if (this._anchor.indexOf("top") > -1)
{
anchor[1] = 0;
}
- else if (this._anchor.indexOf('bottom') > -1)
+ else if (this._anchor.indexOf("bottom") > -1)
{
anchor[1] = 1;
}
return anchor;
}
-
-
}
-
/**
* This map associates units to default letter height.
*
@@ -525,18 +536,17 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
* @private
*/
TextBox._defaultLetterHeightMap = new Map([
- ['cm', 1.0],
- ['deg', 1.0],
- ['degs', 1.0],
- ['degFlatPos', 1.0],
- ['degFlat', 1.0],
- ['norm', 0.1],
- ['height', 0.2],
- ['pix', 20],
- ['pixels', 20]
+ ["cm", 1.0],
+ ["deg", 1.0],
+ ["degs", 1.0],
+ ["degFlatPos", 1.0],
+ ["degFlat", 1.0],
+ ["norm", 0.1],
+ ["height", 0.2],
+ ["pix", 20],
+ ["pixels", 20],
]);
-
/**
* This map associates units to default sizes.
*
@@ -545,13 +555,13 @@ TextBox._defaultLetterHeightMap = new Map([
* @private
*/
TextBox._defaultSizeMap = new Map([
- ['cm', [15.0, -1]],
- ['deg', [15.0, -1]],
- ['degs', [15.0, -1]],
- ['degFlatPos', [15.0, -1]],
- ['degFlat', [15.0, -1]],
- ['norm', [1, -1]],
- ['height', [1, -1]],
- ['pix', [500, -1]],
- ['pixels', [500, -1]]
+ ["cm", [15.0, -1]],
+ ["deg", [15.0, -1]],
+ ["degs", [15.0, -1]],
+ ["degFlatPos", [15.0, -1]],
+ ["degFlat", [15.0, -1]],
+ ["norm", [1, -1]],
+ ["height", [1, -1]],
+ ["pix", [500, -1]],
+ ["pixels", [500, -1]],
]);
diff --git a/src/visual/TextInput.js b/src/visual/TextInput.js
index f99d08b..b343507 100644
--- a/src/visual/TextInput.js
+++ b/src/visual/TextInput.js
@@ -9,7 +9,7 @@
* We are currently using it almost as is but will be making modification in the near future.
*/
-import * as PIXI from 'pixi.js-legacy';
+import * as PIXI from "pixi.js-legacy";
export class TextInput extends PIXI.Container
{
@@ -18,27 +18,27 @@ export class TextInput extends PIXI.Container
super();
this._input_style = Object.assign(
{
- position: 'absolute',
- background: 'none',
- border: 'none',
- outline: 'none',
- text: '',
- transformOrigin: '0 0',
- lineHeight: '1'
+ position: "absolute",
+ background: "none",
+ border: "none",
+ outline: "none",
+ text: "",
+ transformOrigin: "0 0",
+ lineHeight: "1",
},
- styles.input
+ styles.input,
);
if (styles.box)
{
- this._box_generator = typeof styles.box === 'function' ? styles.box : new DefaultBoxGenerator(styles.box);
+ this._box_generator = typeof styles.box === "function" ? styles.box : new DefaultBoxGenerator(styles.box);
}
else
{
this._box_generator = null;
}
- if (this._input_style.hasOwnProperty('multiline'))
+ if (this._input_style.hasOwnProperty("multiline"))
{
this._multiline = !!this._input_style.multiline;
delete this._input_style.multiline;
@@ -52,16 +52,15 @@ export class TextInput extends PIXI.Container
this._previous = {};
this._dom_added = false;
this._dom_visible = true;
- this._placeholder = '';
+ this._placeholder = "";
this._placeholderColor = 0xa9a9a9;
this._selection = [0, 0];
- this._restrict_value = '';
+ this._restrict_value = "";
this._createDOMInput();
this.substituteText = !this._multiline;
- this._setState('DEFAULT');
+ this._setState("DEFAULT");
}
-
// GETTERS & SETTERS
get substituteText()
@@ -103,7 +102,7 @@ export class TextInput extends PIXI.Container
if (this._substituted)
{
this._updateSurrogate();
- this._dom_input.placeholder = '';
+ this._dom_input.placeholder = "";
}
else
{
@@ -120,7 +119,7 @@ export class TextInput extends PIXI.Container
{
this._disabled = disabled;
this._dom_input.disabled = disabled;
- this._setState(disabled ? 'DISABLED' : 'DEFAULT');
+ this._setState(disabled ? "DISABLED" : "DEFAULT");
}
get maxLength()
@@ -131,7 +130,7 @@ export class TextInput extends PIXI.Container
set maxLength(length)
{
this._max_length = length;
- this._dom_input.setAttribute('maxlength', length);
+ this._dom_input.setAttribute("maxlength", length);
}
get restrict()
@@ -145,21 +144,21 @@ export class TextInput extends PIXI.Container
{
regex = regex.toString().slice(1, -1);
- if (regex.charAt(0) !== '^')
+ if (regex.charAt(0) !== "^")
{
- regex = '^' + regex;
+ regex = "^" + regex;
}
- if (regex.charAt(regex.length - 1) !== '$')
+ if (regex.charAt(regex.length - 1) !== "$")
{
- regex = regex + '$';
+ regex = regex + "$";
}
regex = new RegExp(regex);
}
else
{
- regex = new RegExp('^[' + regex + ']*$');
+ regex = new RegExp("^[" + regex + "]*$");
}
this._restrict_regex = regex;
@@ -192,7 +191,6 @@ export class TextInput extends PIXI.Container
}
this._dom_input.focus(options);
-
}
blur()
@@ -211,7 +209,7 @@ export class TextInput extends PIXI.Container
this._input_style[key] = value;
this._dom_input.style[key] = value;
- if (this._substituted && (key === 'fontFamily' || key === 'fontSize'))
+ if (this._substituted && (key === "fontFamily" || key === "fontSize"))
{
this._updateFontMetrics();
}
@@ -228,20 +226,19 @@ export class TextInput extends PIXI.Container
super.destroy(options);
}
-
// SETUP
_createDOMInput()
{
if (this._multiline)
{
- this._dom_input = document.createElement('textarea');
- this._dom_input.style.resize = 'none';
+ this._dom_input = document.createElement("textarea");
+ this._dom_input.style.resize = "none";
}
else
{
- this._dom_input = document.createElement('input');
- this._dom_input.type = 'text';
+ this._dom_input = document.createElement("input");
+ this._dom_input.type = "text";
}
for (let key in this._input_style)
@@ -252,23 +249,23 @@ export class TextInput extends PIXI.Container
_addListeners()
{
- this.on('added', this._onAdded.bind(this));
- this.on('removed', this._onRemoved.bind(this));
- this._dom_input.addEventListener('keydown', this._onInputKeyDown.bind(this));
- this._dom_input.addEventListener('input', this._onInputInput.bind(this));
- this._dom_input.addEventListener('keyup', this._onInputKeyUp.bind(this));
- this._dom_input.addEventListener('focus', this._onFocused.bind(this));
- this._dom_input.addEventListener('blur', this._onBlurred.bind(this));
+ this.on("added", this._onAdded.bind(this));
+ this.on("removed", this._onRemoved.bind(this));
+ this._dom_input.addEventListener("keydown", this._onInputKeyDown.bind(this));
+ this._dom_input.addEventListener("input", this._onInputInput.bind(this));
+ this._dom_input.addEventListener("keyup", this._onInputKeyUp.bind(this));
+ this._dom_input.addEventListener("focus", this._onFocused.bind(this));
+ this._dom_input.addEventListener("blur", this._onBlurred.bind(this));
}
_onInputKeyDown(e)
{
this._selection = [
this._dom_input.selectionStart,
- this._dom_input.selectionEnd
+ this._dom_input.selectionEnd,
];
- this.emit('keydown', e.keyCode);
+ this.emit("keydown", e.keyCode);
}
_onInputInput(e)
@@ -283,30 +280,30 @@ export class TextInput extends PIXI.Container
this._updateSubstitution();
}
- this.emit('input', this.text);
+ this.emit("input", this.text);
}
_onInputKeyUp(e)
{
- this.emit('keyup', e.keyCode);
+ this.emit("keyup", e.keyCode);
}
_onFocused()
{
- this._setState('FOCUSED');
- this.emit('focus');
+ this._setState("FOCUSED");
+ this.emit("focus");
}
_onBlurred()
{
- this._setState('DEFAULT');
- this.emit('blur');
+ this._setState("DEFAULT");
+ this.emit("blur");
}
_onAdded()
{
document.body.appendChild(this._dom_input);
- this._dom_input.style.display = 'none';
+ this._dom_input.style.display = "none";
this._dom_added = true;
}
@@ -326,7 +323,6 @@ export class TextInput extends PIXI.Container
}
}
-
// RENDER & UPDATE
// for pixi v4
@@ -383,7 +379,10 @@ export class TextInput extends PIXI.Container
this._buildBoxCache();
}
- if (this.state == this._previous.state && this._box == this._box_cache[this.state])
+ if (
+ this.state == this._previous.state
+ && this._box == this._box_cache[this.state]
+ )
{
return;
}
@@ -400,7 +399,7 @@ export class TextInput extends PIXI.Container
_updateSubstitution()
{
- if (this.state === 'FOCUSED')
+ if (this.state === "FOCUSED")
{
this._dom_visible = true;
this._surrogate.visible = this.text.length === 0;
@@ -421,8 +420,8 @@ export class TextInput extends PIXI.Container
return;
}
- this._dom_input.style.top = (this._canvas_bounds.top || 0) + 'px';
- this._dom_input.style.left = (this._canvas_bounds.left || 0) + 'px';
+ this._dom_input.style.top = (this._canvas_bounds.top || 0) + "px";
+ this._dom_input.style.left = (this._canvas_bounds.left || 0) + "px";
this._dom_input.style.transform = this._pixiMatrixToCSS(this._getDOMRelativeWorldTransform());
this._dom_input.style.opacity = this.worldAlpha;
this._setDOMInputVisible(this.worldVisible && this._dom_visible);
@@ -444,21 +443,20 @@ export class TextInput extends PIXI.Container
this.text = this._restrict_value;
this._dom_input.setSelectionRange(
this._selection[0],
- this._selection[1]
+ this._selection[1],
);
}
}
-
// STATE COMPAIRSON (FOR PERFORMANCE BENEFITS)
_needsUpdate()
{
return (
- !this._comparePixiMatrices(this.worldTransform, this._previous.world_transform) ||
- !this._compareClientRects(this._canvas_bounds, this._previous.canvas_bounds) ||
- this.worldAlpha != this._previous.world_alpha ||
- this.worldVisible != this._previous.world_visible
+ !this._comparePixiMatrices(this.worldTransform, this._previous.world_transform)
+ || !this._compareClientRects(this._canvas_bounds, this._previous.canvas_bounds)
+ || this.worldAlpha != this._previous.world_alpha
+ || this.worldVisible != this._previous.world_visible
);
}
@@ -466,13 +464,12 @@ export class TextInput extends PIXI.Container
{
let input_bounds = this._getDOMInputBounds();
return (
- !this._previous.input_bounds ||
- input_bounds.width != this._previous.input_bounds.width ||
- input_bounds.height != this._previous.input_bounds.height
+ !this._previous.input_bounds
+ || input_bounds.width != this._previous.input_bounds.width
+ || input_bounds.height != this._previous.input_bounds.height
);
}
-
// INPUT SUBSTITUTION
_createSurrogate()
@@ -480,14 +477,14 @@ export class TextInput extends PIXI.Container
this._surrogate_hitbox = new PIXI.Graphics();
this._surrogate_hitbox.alpha = 0;
this._surrogate_hitbox.interactive = true;
- this._surrogate_hitbox.cursor = 'text';
- this._surrogate_hitbox.on('pointerdown', this._onSurrogateFocus.bind(this));
+ this._surrogate_hitbox.cursor = "text";
+ this._surrogate_hitbox.on("pointerdown", this._onSurrogateFocus.bind(this));
this.addChild(this._surrogate_hitbox);
this._surrogate_mask = new PIXI.Graphics();
this.addChild(this._surrogate_mask);
- this._surrogate = new PIXI.Text('', {});
+ this._surrogate = new PIXI.Text("", {});
this.addChild(this._surrogate);
this._surrogate.mask = this._surrogate_mask;
@@ -509,15 +506,15 @@ export class TextInput extends PIXI.Container
switch (this._surrogate.style.align)
{
- case 'left':
+ case "left":
this._surrogate.x = padding[3];
break;
- case 'center':
+ case "center":
this._surrogate.x = input_bounds.width * 0.5 - this._surrogate.width * 0.5;
break;
- case 'right':
+ case "right":
this._surrogate.x = input_bounds.width - padding[1] - this._surrogate.width;
break;
}
@@ -563,7 +560,7 @@ export class TextInput extends PIXI.Container
_onSurrogateFocus()
{
this._setDOMInputVisible(true);
- //sometimes the input is not being focused by the mouseclick
+ // sometimes the input is not being focused by the mouseclick
setTimeout(this._ensureFocus.bind(this), 10);
}
@@ -583,23 +580,23 @@ export class TextInput extends PIXI.Container
{
switch (key)
{
- case 'color':
+ case "color":
style.fill = this._input_style.color;
break;
- case 'fontFamily':
- case 'fontSize':
- case 'fontWeight':
- case 'fontVariant':
- case 'fontStyle':
+ case "fontFamily":
+ case "fontSize":
+ case "fontWeight":
+ case "fontVariant":
+ case "fontStyle":
style[key] = this._input_style[key];
break;
- case 'letterSpacing':
+ case "letterSpacing":
style.letterSpacing = parseFloat(this._input_style.letterSpacing);
break;
- case 'textAlign':
+ case "textAlign":
style.align = this._input_style.textAlign;
break;
}
@@ -626,7 +623,7 @@ export class TextInput extends PIXI.Container
if (this._input_style.padding && this._input_style.padding.length > 0)
{
- let components = this._input_style.padding.trim().split(' ');
+ let components = this._input_style.padding.trim().split(" ");
if (components.length == 1)
{
@@ -641,7 +638,7 @@ export class TextInput extends PIXI.Container
}
else if (components.length == 4)
{
- let padding = components.map(component =>
+ let padding = components.map((component) =>
{
return parseFloat(component);
});
@@ -655,7 +652,17 @@ export class TextInput extends PIXI.Container
_deriveSurrogateText()
{
- return this._dom_input.value.length === 0 ? this._placeholder : this._dom_input.value;
+ if (this._dom_input.value.length === 0)
+ {
+ return this._placeholder;
+ }
+
+ if (this._dom_input.type == "password")
+ {
+ return "•".repeat(this._dom_input.value.length);
+ }
+
+ return this._dom_input.value;
}
_updateFontMetrics()
@@ -666,25 +673,23 @@ export class TextInput extends PIXI.Container
this._font_metrics = PIXI.TextMetrics.measureFont(font);
}
-
// CACHING OF INPUT BOX GRAPHICS
_buildBoxCache()
{
this._destroyBoxCache();
- let states = ['DEFAULT', 'FOCUSED', 'DISABLED'];
+ let states = ["DEFAULT", "FOCUSED", "DISABLED"];
let input_bounds = this._getDOMInputBounds();
states.forEach((state) =>
- {
- this._box_cache[state] = this._box_generator(
- input_bounds.width,
- input_bounds.height,
- state
- );
- }
- );
+ {
+ this._box_cache[state] = this._box_generator(
+ input_bounds.width,
+ input_bounds.height,
+ state,
+ );
+ });
this._previous.input_bounds = input_bounds;
}
@@ -705,7 +710,6 @@ export class TextInput extends PIXI.Container
}
}
-
// HELPER FUNCTIONS
_hasFocus()
@@ -715,13 +719,13 @@ export class TextInput extends PIXI.Container
_setDOMInputVisible(visible)
{
- this._dom_input.style.display = visible ? 'block' : 'none';
+ this._dom_input.style.display = visible ? "block" : "none";
}
_getCanvasBounds()
{
let rect = this._last_renderer.view.getBoundingClientRect();
- let bounds = {top: rect.top, left: rect.left, width: rect.width, height: rect.height};
+ let bounds = { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
bounds.left += window.scrollX;
bounds.top += window.scrollY;
return bounds;
@@ -739,8 +743,8 @@ export class TextInput extends PIXI.Container
let org_transform = this._dom_input.style.transform;
let org_display = this._dom_input.style.display;
- this._dom_input.style.transform = '';
- this._dom_input.style.display = 'block';
+ this._dom_input.style.transform = "";
+ this._dom_input.style.display = "block";
let bounds = this._dom_input.getBoundingClientRect();
this._dom_input.style.transform = org_transform;
this._dom_input.style.display = org_display;
@@ -759,14 +763,13 @@ export class TextInput extends PIXI.Container
let matrix = this.worldTransform.clone();
matrix.scale(this._resolution, this._resolution);
- matrix.scale(canvas_bounds.width / this._last_renderer.width,
- canvas_bounds.height / this._last_renderer.height);
+ matrix.scale(canvas_bounds.width / this._last_renderer.width, canvas_bounds.height / this._last_renderer.height);
return matrix;
}
_pixiMatrixToCSS(m)
{
- return 'matrix(' + [m.a, m.b, m.c, m.d, m.tx, m.ty].join(',') + ')';
+ return "matrix(" + [m.a, m.b, m.c, m.d, m.tx, m.ty].join(",") + ")";
}
_comparePixiMatrices(m1, m2)
@@ -776,12 +779,12 @@ export class TextInput extends PIXI.Container
return false;
}
return (
- m1.a == m2.a &&
- m1.b == m2.b &&
- m1.c == m2.c &&
- m1.d == m2.d &&
- m1.tx == m2.tx &&
- m1.ty == m2.ty
+ m1.a == m2.a
+ && m1.b == m2.b
+ && m1.c == m2.c
+ && m1.d == m2.d
+ && m1.tx == m2.tx
+ && m1.ty == m2.ty
);
}
@@ -792,20 +795,17 @@ export class TextInput extends PIXI.Container
return false;
}
return (
- r1.left == r2.left &&
- r1.top == r2.top &&
- r1.width == r2.width &&
- r1.height == r2.height
+ r1.left == r2.left
+ && r1.top == r2.top
+ && r1.width == r2.width
+ && r1.height == r2.height
);
}
-
-
}
-
function DefaultBoxGenerator(styles)
{
- styles = styles || {fill: 0xcccccc};
+ styles = styles || { fill: 0xcccccc };
if (styles.default)
{
@@ -819,7 +819,7 @@ function DefaultBoxGenerator(styles)
styles.default = styles.focused = styles.disabled = temp_styles;
}
- return function (w, h, state)
+ return function(w, h, state)
{
let style = styles[state.toLowerCase()];
let box = new PIXI.Graphics();
@@ -834,7 +834,7 @@ function DefaultBoxGenerator(styles)
box.lineStyle(
style.stroke.width ?? 1,
style.stroke.color ?? 0,
- style.stroke.alpha ?? 1
+ style.stroke.alpha ?? 1,
);
}
diff --git a/src/visual/TextStim.js b/src/visual/TextStim.js
index d41e488..b48da12 100644
--- a/src/visual/TextStim.js
+++ b/src/visual/TextStim.js
@@ -7,13 +7,12 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {VisualStim} from './VisualStim';
-import {Color} from '../util/Color';
-import {ColorMixin} from '../util/ColorMixin';
-import * as util from '../util/Util';
-
+import * as PIXI from "pixi.js-legacy";
+import { Color } from "../util/Color.js";
+import { ColorMixin } from "../util/ColorMixin.js";
+import { to_pixiPoint } from "../util/Pixi.js";
+import * as util from "../util/Util.js";
+import { VisualStim } from "./VisualStim.js";
/**
* @name module:visual.TextStim
@@ -48,9 +47,34 @@ import * as util from '../util/Util';
*/
export class TextStim extends util.mix(VisualStim).with(ColorMixin)
{
- constructor({name, win, text, font, pos, color, opacity, depth, contrast, units, ori, height, bold, italic, alignHoriz, alignVert, wrapWidth, flipHoriz, flipVert, clipMask, autoDraw, autoLog} = {})
+ constructor(
+ {
+ name,
+ win,
+ text,
+ font,
+ pos,
+ color,
+ opacity,
+ depth,
+ contrast,
+ units,
+ ori,
+ height,
+ bold,
+ italic,
+ alignHoriz,
+ alignVert,
+ wrapWidth,
+ flipHoriz,
+ flipVert,
+ clipMask,
+ autoDraw,
+ autoLog,
+ } = {},
+ )
{
- super({name, win, units, ori, opacity, depth, pos, clipMask, autoDraw, autoLog});
+ super({ name, win, units, ori, opacity, depth, pos, clipMask, autoDraw, autoLog });
// callback to deal with text metrics invalidation:
const onChange = (withPixi = false, withBoundingBox = false, withMetrics = false) =>
@@ -68,81 +92,80 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
// text and font:
this._addAttribute(
- 'text',
+ "text",
text,
- 'Hello World',
- onChange(true, true, true)
+ "Hello World",
+ onChange(true, true, true),
);
this._addAttribute(
- 'alignHoriz',
+ "alignHoriz",
alignHoriz,
- 'center',
- onChange(true, true, true)
+ "center",
+ onChange(true, true, true),
);
this._addAttribute(
- 'alignVert',
+ "alignVert",
alignVert,
- 'center',
- onChange(true, true, true)
+ "center",
+ onChange(true, true, true),
);
this._addAttribute(
- 'flipHoriz',
+ "flipHoriz",
flipHoriz,
false,
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'flipVert',
+ "flipVert",
flipVert,
false,
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'font',
+ "font",
font,
- 'Arial',
- this._onChange(true, true)
+ "Arial",
+ this._onChange(true, true),
);
this._addAttribute(
- 'height',
+ "height",
height,
this._getDefaultLetterHeight(),
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'wrapWidth',
+ "wrapWidth",
wrapWidth,
this._getDefaultWrapWidth(),
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'bold',
+ "bold",
bold,
false,
- onChange(true, true, true)
+ onChange(true, true, true),
);
this._addAttribute(
- 'italic',
+ "italic",
italic,
false,
- onChange(true, true, true)
+ onChange(true, true, true),
);
// color:
this._addAttribute(
- 'color',
+ "color",
color,
- 'white',
- this._onChange(true, false)
+ "white",
+ this._onChange(true, false),
);
this._addAttribute(
- 'contrast',
+ "contrast",
contrast,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
-
// estimate the bounding box (using TextMetrics):
this._estimateBoundingBox();
@@ -152,8 +175,6 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
}
}
-
-
/**
* Get the metrics estimated for the text and style.
*
@@ -165,7 +186,7 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
*/
getTextMetrics()
{
- if (typeof this._textMetrics === 'undefined')
+ if (typeof this._textMetrics === "undefined")
{
this._textMetrics = PIXI.TextMetrics.measureText(this._text, this._getTextStyle());
}
@@ -173,8 +194,6 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
return this._textMetrics;
}
-
-
/**
* Get the default letter height given the stimulus' units.
*
@@ -186,20 +205,18 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
{
const height = TextStim._defaultLetterHeightMap.get(this._units);
- if (typeof height === 'undefined')
+ if (typeof height === "undefined")
{
throw {
- origin: 'TextStim._getDefaultLetterHeight',
- context: 'when getting the default height of TextStim: ' + this._name,
- error: 'no default letter height for unit: ' + this._units
+ origin: "TextStim._getDefaultLetterHeight",
+ context: "when getting the default height of TextStim: " + this._name,
+ error: "no default letter height for unit: " + this._units,
};
}
return height;
}
-
-
/**
* Get the default wrap width given the stimulus' units.
*
@@ -211,20 +228,18 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
{
const wrapWidth = TextStim._defaultWrapWidthMap.get(this._units);
- if (typeof wrapWidth === 'undefined')
+ if (typeof wrapWidth === "undefined")
{
throw {
- origin: 'TextStim._getDefaultWrapWidth',
- context: 'when getting the default wrap width of TextStim: ' + this._name,
- error: 'no default wrap width for unit: ' + this._units
+ origin: "TextStim._getDefaultWrapWidth",
+ context: "when getting the default wrap width of TextStim: " + this._name,
+ error: "no default wrap width for unit: " + this._units,
};
}
return wrapWidth;
}
-
-
/**
* Estimate the bounding box.
*
@@ -237,11 +252,11 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
{
// size of the text, irrespective of the orientation:
const textMetrics = this.getTextMetrics();
- const textSize = util.to_unit(
+ const textSize = util.to_unit(
[textMetrics.width, textMetrics.height],
- 'pix',
+ "pix",
this._win,
- this._units
+ this._units,
);
// take the alignment into account:
@@ -250,14 +265,12 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
this._pos[0] - anchor[0] * textSize[0],
this._pos[1] - anchor[1] * textSize[1],
textSize[0],
- textSize[1]
+ textSize[1],
);
// TODO take the orientation into account
}
-
-
/**
* Get the PIXI Text Style applied to the PIXI.Text
*
@@ -269,17 +282,15 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
return new PIXI.TextStyle({
fontFamily: this._font,
fontSize: Math.round(this._getLengthPix(this._height)),
- fontWeight: (this._bold) ? 'bold' : 'normal',
- fontStyle: (this._italic) ? 'italic' : 'normal',
+ fontWeight: (this._bold) ? "bold" : "normal",
+ fontStyle: (this._italic) ? "italic" : "normal",
fill: this.getContrastedColor(new Color(this._color), this._contrast).hex,
align: this._alignHoriz,
- wordWrap: (typeof this._wrapWidth !== 'undefined'),
- wordWrapWidth: (typeof this._wrapWidth !== 'undefined') ? this._getHorLengthPix(this._wrapWidth) : 0
+ wordWrap: (typeof this._wrapWidth !== "undefined"),
+ wordWrapWidth: (typeof this._wrapWidth !== "undefined") ? this._getHorLengthPix(this._wrapWidth) : 0,
});
}
-
-
/**
* Update the stimulus, if necessary.
*
@@ -300,7 +311,7 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
{
this._needPixiUpdate = false;
- if (typeof this._pixi !== 'undefined')
+ if (typeof this._pixi !== "undefined")
{
this._pixi.destroy(true);
}
@@ -314,7 +325,7 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
this._pixi.scale.y = this._flipVert ? 1 : -1;
this._pixi.rotation = this._ori * Math.PI / 180;
- this._pixi.position = util.to_pixiPoint(this.pos, this.units, this.win);
+ this._pixi.position = to_pixiPoint(this.pos, this.units, this.win);
this._pixi.alpha = this._opacity;
this._pixi.zIndex = this._depth;
@@ -325,7 +336,7 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
// update the size attributes:
this._size = [
this._getLengthUnits(Math.abs(this._pixi.width)),
- this._getLengthUnits(Math.abs(this._pixi.height))
+ this._getLengthUnits(Math.abs(this._pixi.height)),
];
// refine the estimate of the bounding box:
@@ -333,12 +344,10 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
this._pos[0] - anchor[0] * this._size[0],
this._pos[1] - anchor[1] * this._size[1],
this._size[0],
- this._size[1]
+ this._size[1],
);
}
-
-
/**
* Convert the alignment attributes into an anchor.
*
@@ -353,36 +362,33 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
switch (this._alignHoriz)
{
- case 'left':
+ case "left":
anchor.push(0);
break;
- case 'right':
+ case "right":
anchor.push(1);
break;
default:
- case 'center':
+ case "center":
anchor.push(0.5);
}
switch (this._alignVert)
{
- case 'top':
+ case "top":
anchor.push(0);
break;
- case 'bottom':
+ case "bottom":
anchor.push(1);
break;
default:
- case 'center':
+ case "center":
anchor.push(0.5);
}
return anchor;
}
-
}
-
-
/**
* This map associates units to default letter height.
*
@@ -391,19 +397,17 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
* @private
*/
TextStim._defaultLetterHeightMap = new Map([
- ['cm', 1.0],
- ['deg', 1.0],
- ['degs', 1.0],
- ['degFlatPos', 1.0],
- ['degFlat', 1.0],
- ['norm', 0.1],
- ['height', 0.2],
- ['pix', 20],
- ['pixels', 20]
+ ["cm", 1.0],
+ ["deg", 1.0],
+ ["degs", 1.0],
+ ["degFlatPos", 1.0],
+ ["degFlat", 1.0],
+ ["norm", 0.1],
+ ["height", 0.2],
+ ["pix", 20],
+ ["pixels", 20],
]);
-
-
/**
* This map associates units to default wrap width.
*
@@ -412,13 +416,13 @@ TextStim._defaultLetterHeightMap = new Map([
* @private
*/
TextStim._defaultWrapWidthMap = new Map([
- ['cm', 15.0],
- ['deg', 15.0],
- ['degs', 15.0],
- ['degFlatPos', 15.0],
- ['degFlat', 15.0],
- ['norm', 1],
- ['height', 1],
- ['pix', 500],
- ['pixels', 500]
+ ["cm", 15.0],
+ ["deg", 15.0],
+ ["degs", 15.0],
+ ["degFlatPos", 15.0],
+ ["degFlat", 15.0],
+ ["norm", 1],
+ ["height", 1],
+ ["pix", 500],
+ ["pixels", 500],
]);
diff --git a/src/visual/VisualStim.js b/src/visual/VisualStim.js
index 4f1b815..02ae2d9 100644
--- a/src/visual/VisualStim.js
+++ b/src/visual/VisualStim.js
@@ -7,12 +7,10 @@
* @license Distributed under the terms of the MIT License
*/
-
-import * as PIXI from 'pixi.js-legacy';
-import {MinimalStim} from '../core/MinimalStim';
-import {WindowMixin} from '../core/WindowMixin';
-import * as util from '../util/Util';
-
+import * as PIXI from "pixi.js-legacy";
+import { MinimalStim } from "../core/MinimalStim.js";
+import { WindowMixin } from "../core/WindowMixin.js";
+import * as util from "../util/Util.js";
/**
* Base class for all visual stimuli.
@@ -36,55 +34,54 @@ import * as util from '../util/Util';
*/
export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
{
- constructor({name, win, units, ori, opacity, depth, pos, size, clipMask, autoDraw, autoLog} = {})
+ constructor({ name, win, units, ori, opacity, depth, pos, size, clipMask, autoDraw, autoLog } = {})
{
- super({win, name, autoDraw, autoLog});
+ super({ win, name, autoDraw, autoLog });
this._addAttribute(
- 'units',
+ "units",
units,
- (typeof win !== 'undefined' && win !== null) ? win.units : 'height',
- this._onChange(true, true)
+ (typeof win !== "undefined" && win !== null) ? win.units : "height",
+ this._onChange(true, true),
);
this._addAttribute(
- 'pos',
+ "pos",
pos,
- [0, 0]
+ [0, 0],
);
this._addAttribute(
- 'size',
+ "size",
size,
- undefined
+ undefined,
);
this._addAttribute(
- 'ori',
+ "ori",
ori,
- 0.0
+ 0.0,
);
this._addAttribute(
- 'opacity',
+ "opacity",
opacity,
1.0,
- this._onChange(true, false)
+ this._onChange(true, false),
);
this._addAttribute(
- 'depth',
+ "depth",
depth,
0,
- this._onChange(false, false)
+ this._onChange(false, false),
);
this._addAttribute(
- 'clipMask',
+ "clipMask",
clipMask,
null,
- this._onChange(false, false)
+ this._onChange(false, false),
);
// bounding box of the stimulus, in stimulus units
// note: boundingBox does not take the orientation into account
- this._addAttribute('boundingBox', PIXI.Rectangle.EMPTY);
+ this._addAttribute("boundingBox", PIXI.Rectangle.EMPTY);
-
// the stimulus need to be updated:
this._needUpdate = true;
@@ -92,8 +89,6 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
this._needPixiUpdate = true;
}
-
-
/**
* Force a refresh of the stimulus.
*
@@ -107,8 +102,6 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
this._onChange(true, true)();
}
-
-
/**
* Setter for the size attribute.
*
@@ -120,7 +113,7 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
setSize(size, log = false)
{
// size is either undefined, null, or a tuple of numbers:
- if (typeof size !== 'undefined' && size !== null)
+ if (typeof size !== "undefined" && size !== null)
{
size = util.toNumerical(size);
if (!Array.isArray(size))
@@ -129,7 +122,7 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
}
}
- const hasChanged = this._setAttribute('size', size, log);
+ const hasChanged = this._setAttribute("size", size, log);
if (hasChanged)
{
@@ -137,8 +130,6 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
}
}
-
-
/**
* Setter for the orientation attribute.
*
@@ -149,20 +140,17 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
*/
setOri(ori, log = false)
{
- const hasChanged = this._setAttribute('ori', ori, log);
+ const hasChanged = this._setAttribute("ori", ori, log);
if (hasChanged)
{
let radians = -ori * 0.017453292519943295;
- this._rotationMatrix = [[Math.cos(radians), -Math.sin(radians)],
- [Math.sin(radians), Math.cos(radians)]];
+ this._rotationMatrix = [[Math.cos(radians), -Math.sin(radians)], [Math.sin(radians), Math.cos(radians)]];
this._onChange(true, true)();
}
}
-
-
/**
* Setter for the position attribute.
*
@@ -174,20 +162,18 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
setPos(pos, log = false)
{
const prevPos = this._pos;
- const hasChanged = this._setAttribute('pos', util.toNumerical(pos), log);
+ const hasChanged = this._setAttribute("pos", util.toNumerical(pos), log);
if (hasChanged)
{
this._needUpdate = true;
-
+
// update the bounding box, without calling _estimateBoundingBox:
this._boundingBox.x += this._pos[0] - prevPos[0];
this._boundingBox.y += this._pos[1] - prevPos[1];
}
}
-
-
/**
* Determine whether an object is inside the bounding box of the stimulus.
*
@@ -202,12 +188,12 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
// get the position of the object, in pixel coordinates:
const objectPos_px = util.getPositionFromObject(object, units);
- if (typeof objectPos_px === 'undefined')
+ if (typeof objectPos_px === "undefined")
{
throw {
- origin: 'VisualStim.contains',
- context: 'when determining whether VisualStim: ' + this._name + ' contains object: ' + util.toString(object),
- error: 'unable to determine the position of the object'
+ origin: "VisualStim.contains",
+ context: "when determining whether VisualStim: " + this._name + " contains object: " + util.toString(object),
+ error: "unable to determine the position of the object",
};
}
@@ -215,8 +201,6 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
return this._getBoundingBox_px().contains(objectPos_px[0], objectPos_px[1]);
}
-
-
/**
* Estimate the bounding box.
*
@@ -227,14 +211,12 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
_estimateBoundingBox()
{
throw {
- origin: 'VisualStim._estimateBoundingBox',
+ origin: "VisualStim._estimateBoundingBox",
context: `when estimating the bounding box of visual stimulus: ${this._name}`,
- error: 'this method is abstract and should not be called.'
+ error: "this method is abstract and should not be called.",
};
}
-
-
/**
* Get the bounding box in pixel coordinates
*
@@ -245,37 +227,35 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
*/
_getBoundingBox_px()
{
- if (this._units === 'pix')
+ if (this._units === "pix")
{
return this._boundingBox.clone();
}
- else if (this._units === 'norm')
+ else if (this._units === "norm")
{
return new PIXI.Rectangle(
this._boundingBox.x * this._win.size[0] / 2,
this._boundingBox.y * this._win.size[1] / 2,
this._boundingBox.width * this._win.size[0] / 2,
- this._boundingBox.height * this._win.size[1] / 2
+ this._boundingBox.height * this._win.size[1] / 2,
);
}
- else if (this._units === 'height')
+ else if (this._units === "height")
{
const minSize = Math.min(this._win.size[0], this._win.size[1]);
return new PIXI.Rectangle(
this._boundingBox.x * minSize,
this._boundingBox.y * minSize,
this._boundingBox.width * minSize,
- this._boundingBox.height * minSize
+ this._boundingBox.height * minSize,
);
}
else
{
- throw Object.assign(response, {error: `unknown units: ${this._units}`});
+ throw Object.assign(response, { error: `unknown units: ${this._units}` });
}
}
-
-
/**
* Generate a callback that prepares updates to the stimulus.
* This is typically called in the constructor of a stimulus, when attributes are added with _addAttribute.
@@ -302,5 +282,4 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
}
};
}
-
}
diff --git a/src/visual/index.js b/src/visual/index.js
index 9100d08..2794a47 100644
--- a/src/visual/index.js
+++ b/src/visual/index.js
@@ -1,15 +1,14 @@
-export * from './ButtonStim.js';
-export * from './Form.js';
-export * from './ImageStim.js';
-export * from './MovieStim.js';
-export * from './Polygon.js';
-export * from './Rect.js';
-export * from './ShapeStim.js';
-export * from './Slider.js';
-export * from './TextBox.js';
-export * from './TextInput.js';
-export * from './TextStim.js';
-export * from './VisualStim.js';
-
-export * from './Camera.js';
-export * from './FaceDetector.js';
+export * from "./ButtonStim.js";
+export * from "./Form.js";
+export * from "./ImageStim.js";
+export * from "./MovieStim.js";
+export * from "./Polygon.js";
+export * from "./Rect.js";
+export * from "./ShapeStim.js";
+export * from "./Slider.js";
+export * from "./TextBox.js";
+export * from "./TextInput.js";
+export * from "./TextStim.js";
+export * from "./VisualStim.js";
+export * from "./Camera.js";
+export * from "./FaceDetector.js";