1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-12 16:48:10 +00:00

Merge branch 'psychopy:main' into master

This commit is contained in:
Alain Pitiot 2021-06-01 11:03:50 +02:00
commit 1c8bfcf4b4
25 changed files with 3427 additions and 8800 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
dist dist
node_modules out
node_modules

11737
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,69 +8,42 @@
"name": "Alain Pitiot" "name": "Alain Pitiot"
}, },
"type": "module", "type": "module",
"main": "src/index.js", "exports": {
".": "./src/index.js",
"./css": "./src/index.css",
"./package": "./package.json"
},
"main": "./src/index.js",
"scripts": { "scripts": {
"build": "npm run build:js && npm run build:css && npm run build:docs", "build": "npm run build:js && npm run build:css && npm run build:docs",
"build:css": "npx cross-var postcss -o dist/psychojs-$npm_package_version.css src/index.css", "build:css": "node ./scripts/build.css.cjs",
"build:docs": "jsdoc src -r -d docs", "build:docs": "jsdoc src -r -d docs",
"build:js": "rollup -c", "build:js": "node ./scripts/build.js.cjs",
"lint": "npm run lint:js && npm run lint:css", "lint": "npm run lint:js && npm run lint:css",
"lint:css": "stylelint src/**/*.css", "lint:css": "csslint src",
"lint:js": "eslint rollup.config.js src/**/*.js", "lint:js": "eslint src",
"lint:scripts": "eslint scripts --ext .cjs",
"start": "npm run build" "start": "npm run build"
}, },
"babel": { "dependencies": {
"presets": [ "howler": "^2.2.1",
[ "log4javascript": "github:Ritzlgrmft/log4javascript",
"@babel/preset-env", "moment": "^2.29.1",
{ "pako": "^1.0.10",
"modules": false, "pixi.js-legacy": "^6.0.4",
"targets": { "preload-js": "^0.6.3",
"ie": 11 "seedrandom": "^3.0.5",
}, "tone": "^14.7.77",
"spec": true, "xlsx": "^0.17.0"
"forceAllTransforms": true,
"debug": true
}
]
]
},
"browserslist": [
"last 2 versions"
],
"stylelint": {
"extends": "stylelint-config-standard",
"rules": {
"no-descending-specificity": [
true,
{
"severity": "warning"
}
]
}
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.3", "csslint": "^1.0.5",
"@babel/preset-env": "^7.12.1", "esbuild": "^0.12.1",
"@rollup/plugin-babel": "^5.2.1", "eslint": "^7.26.0",
"cross-var": "^1.1.0", "jsdoc": "^3.6.7"
"cssnano": "^4.1.10",
"eslint": "^7.24.0",
"jsdoc": "^3.6.6",
"postcss": "^8.1.3",
"postcss-cli": "^8.1.0",
"postcss-preset-env": "^6.7.0",
"rollup": "^2.32.1",
"stylelint": "^13.7.2",
"stylelint-config-standard": "^20.0.0",
"terser": "^5.3.8"
}, },
"postcss": { "engines": {
"plugins": { "node": ">=14.15.0",
"postcss-preset-env": {}, "npm": ">=6.14.8"
"cssnano": {
"autoprefixer": false
}
}
} }
} }

View File

@ -1,212 +0,0 @@
// ES native imports courtesy of using type module in 'package.json'
import path from 'path';
import fs from 'fs';
import { minify } from 'terser';
import babel from '@rollup/plugin-babel';
import pkg from './package.json';
// Manually set default version here for easier
// diffing when comparing to original build script output
const { VERSION: version = pkg.version } = process.env;
// Enabled in the original, even though
// source maps missing for sample provided
const sourcemap = false;
// Might be 'build' or similar
const destination = './dist';
// Could be 'src' or 'lib'
const source = './src';
// Start fresh
try {
if (fs.existsSync(destination)) {
// Clear out JS files before rebuilding
const contents = fs.readdirSync(destination).filter(item => item.endsWith('js'));
for (const item of contents) {
const target = path.join(destination, item);
const stat = fs.statSync(target);
// Delete
fs.unlinkSync(target);
}
} else {
// Create 'dist' if missing
fs.mkdirSync(destination);
}
} catch (error) {
console.error(error);
}
// For sorting legacy/IE11 bundle components
const orderOfAppearance = [ 'util', 'data', 'core', 'visual', 'sound' ];
const last = [ ...orderOfAppearance ].pop();
const footer = `
// Add a few top level variables for convenience, this makes it
// possible to eg. use "return Scheduler.Event.NEXT;" instead of "util.Scheduler.Event.NEXT;"
PsychoJS = core.PsychoJS;
TrialHandler = data.TrialHandler;
Scheduler = util.Scheduler;`;
const plugins = [
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
include: `${destination}/*.iife.js`
}),
minifier({
compress: false,
mangle: false,
output: {
beautify: true
},
sourceMap: false,
toplevel: false
})
];
// List source directory contents
const components = fs.readdirSync(source)
// Need subdirectories only
.filter((item) => {
const target = path.join(source, item);
const stat = fs.statSync(target);
return stat.isDirectory();
})
// Put in order
.sort((a, b) => orderOfAppearance.indexOf(a) - orderOfAppearance.indexOf(b))
// Prepare an output object for each component module
.map((component, _, contents) => ({
// So I don't have to specify full paths
external: (id) => {
// Decompose current component path
const segments = id.split('/');
// Mark as external if contents within source
// directory tree, excluding the current component
return contents
.filter(item => item !== component)
.some(item => segments.includes(item));
},
input: `${source}/${component}/index.js`,
// Disable circular dependency warnings
onwarn,
output: [
{
file: `${destination}/${component}-${version}.js`,
format: 'module',
globals: {
performance: 'performance'
},
// Find which module the import points to
// and fix path in place
paths: (id) => {
const name = findName(id, contents);
return `./${name}-${version}.js`;
},
sourcemap,
},
{
esModule: false,
file: `${destination}/${component}-${version}.iife.js`,
format: 'iife',
globals: id => findName(id, contents),
name: component,
paths: (id) => {
const name = findName(id, contents);
return `./${name}-${version}.iife.js`;
},
sourcemap,
plugins: [
appender({
target: `${destination}/psychojs-${version}.js`,
// Mirrors rollup's 'outputOptions' hook
outputOptions: (options) => {
if (options.file.includes(last)) {
options.footer = footer;
}
return options;
}
})
]
}
],
plugins
})
);
export default [
...components,
{
// Add a UMD build for Thomas
input: `${source}/index.js`,
onwarn,
output: {
file: `${destination}/psychojs-${version}.umd.js`,
format: 'umd',
name: 'psychojs'
},
plugins
}
];
// https://rollupjs.org/guide/en/#onwarn
function onwarn(message, warn) {
// Skip circular dependency warnings
if (message.code === 'CIRCULAR_DEPENDENCY') {
return;
}
warn(message);
}
// Helper for extracting module name from contents array by rollup id (path to file)
function findName(id, contents) {
return id.split(path.sep).find(item => contents.includes(item));
}
// Minimal terser plugin
function minifier(options) {
return {
name: 'minifier',
async renderChunk(code) {
try {
// Includes code and map keys
const result = await minify(code, options);
return result;
} catch (error) {
throw error;
}
}
};
}
// Custom plugin for cancatenating IIFE's sans cat(1)
function appender({ target = '', outputOptions = () => {} } = {}) {
return {
name: 'appender',
outputOptions: (options) => outputOptions(options),
async generateBundle(options, bundle) {
const { file } = options;
const id = file.split('/').pop();
const { code } = bundle[id];
// Should be expected to throw if `target` missing
fs.appendFile(target, code, (error) => {
if (error) {
throw error;
}
});
// Prevent write out
delete bundle[id];
}
};
}

13
scripts/build.css.cjs Normal file
View File

@ -0,0 +1,13 @@
const { buildSync } = require('esbuild');
const pkg = require('psychojs/package');
const versionMaybe = process.env.npm_config_outver;
const dirMaybe = process.env.npm_config_outdir;
const [,,, dir = dirMaybe || 'out', version = versionMaybe || pkg.version] = process.argv;
buildSync({
bundle: true,
entryPoints: ['src/index.css'],
minify: true,
outfile: `./${dir}/psychojs-${version}.css`
});

14
scripts/build.js.cjs Normal file
View File

@ -0,0 +1,14 @@
const { buildSync } = require('esbuild');
const pkg = require('psychojs/package');
const versionMaybe = process.env.npm_config_outver;
const dirMaybe = process.env.npm_config_outdir;
const [,,, dir = dirMaybe || 'out', version = versionMaybe || pkg.version] = process.argv;
buildSync({
bundle: true,
entryPoints: ['src/index.js'],
format: 'esm',
minify: true,
outfile: `./${dir}/psychojs-${version}.js`
});

View File

@ -8,7 +8,7 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import * as Tone from 'tone';
import {PsychoJS} from './PsychoJS'; import {PsychoJS} from './PsychoJS';
import {ServerManager} from './ServerManager'; import {ServerManager} from './ServerManager';
import {Scheduler} from '../util/Scheduler'; import {Scheduler} from '../util/Scheduler';
@ -113,7 +113,9 @@ export class GUI
} }
// prepare jquery UI dialog box: // prepare jquery UI dialog box:
let htmlCode = '<div id="expDialog" title="' + title + '">'; let htmlCode =
'<div id="expDialog" title="' + title + '">' +
'<p class="validateTips">Fields marked with an asterisk (*) are required.</p>';
// uncomment for older version of the library: // uncomment for older version of the library:
// htmlCode += '<p style="font-size: 0.8em; padding: 0.5em; margin-bottom: 0.5em; color: #FFAA00; border: 1px solid #FFAA00;">&#9888; This experiment uses a deprecated version of the PsychoJS library. Consider updating to a newer version (e.g. by updating PsychoPy and re-exporting the experiment).</p>'+ // htmlCode += '<p style="font-size: 0.8em; padding: 0.5em; margin-bottom: 0.5em; color: #FFAA00; border: 1px solid #FFAA00;">&#9888; This experiment uses a deprecated version of the PsychoJS library. Consider updating to a newer version (e.g. by updating PsychoPy and re-exporting the experiment).</p>'+
@ -181,7 +183,7 @@ export class GUI
} }
htmlCode += '</select>'; htmlCode += '</select>';
$('#' + keyId).selectmenu({classes: {}}); jQuery('#' + keyId).selectmenu({classes: {}});
} }
// otherwise we use a single string input: // otherwise we use a single string input:
@ -209,7 +211,7 @@ export class GUI
// the dialog box: // the dialog box:
if (typeof logoUrl === 'string') if (typeof logoUrl === 'string')
{ {
$("#dialog-logo").on('load', () => jQuery("#dialog-logo").on('load', () =>
{ {
self._onDialogOpen('#expDialog')(); self._onDialogOpen('#expDialog')();
}); });
@ -231,7 +233,7 @@ export class GUI
self._dialogComponent.button = 'Cancel'; self._dialogComponent.button = 'Cancel';
self._estimateDialogScalingFactor(); self._estimateDialogScalingFactor();
const dialogSize = self._getDialogSize(); const dialogSize = self._getDialogSize();
$("#expDialog").dialog({ jQuery("#expDialog").dialog({
width: dialogSize[0], width: dialogSize[0],
maxHeight: dialogSize[1], maxHeight: dialogSize[1],
@ -248,7 +250,7 @@ export class GUI
click: function () click: function ()
{ {
self._dialogComponent.button = 'Cancel'; self._dialogComponent.button = 'Cancel';
$("#expDialog").dialog('close'); jQuery("#expDialog").dialog('close');
} }
}, },
{ {
@ -270,7 +272,7 @@ export class GUI
self._dialogComponent.button = 'OK'; self._dialogComponent.button = 'OK';
$("#expDialog").dialog('close'); jQuery("#expDialog").dialog('close');
// Tackle browser demands on having user action initiate audio context // Tackle browser demands on having user action initiate audio context
Tone.start(); Tone.start();
@ -290,8 +292,8 @@ export class GUI
// close is called by both buttons and when the user clicks on the cross: // close is called by both buttons and when the user clicks on the cross:
close: function () close: function ()
{ {
//$.unblockUI(); //jQuery.unblockUI();
$(this).dialog('destroy').remove(); jQuery(this).dialog('destroy').remove();
self._dialogComponent.status = PsychoJS.Status.FINISHED; self._dialogComponent.status = PsychoJS.Status.FINISHED;
} }
@ -310,11 +312,11 @@ export class GUI
// block UI until user has pressed dialog button: // block UI until user has pressed dialog button:
// note: block UI does not allow for text to be entered in the dialog form boxes, alas! // note: block UI does not allow for text to be entered in the dialog form boxes, alas!
//$.blockUI({ message: "", baseZ: 1}); //jQuery.blockUI({ message: "", baseZ: 1});
// show dialog box: // show dialog box:
$("#progressbar").progressbar({value: self._progressBarCurrentValue}); jQuery("#progressbar").progressbar({value: self._progressBarCurrentValue});
$("#progressbar").progressbar("option", "max", self._progressBarMax); jQuery("#progressbar").progressbar("option", "max", self._progressBarMax);
} }
if (self._dialogComponent.status === PsychoJS.Status.FINISHED) if (self._dialogComponent.status === PsychoJS.Status.FINISHED)
@ -357,12 +359,12 @@ export class GUI
{ {
// close the previously opened dialog box, if there is one: // close the previously opened dialog box, if there is one:
const expDialog = $("#expDialog"); const expDialog = jQuery("#expDialog");
if (expDialog.length) if (expDialog.length)
{ {
expDialog.dialog("destroy").remove(); expDialog.dialog("destroy").remove();
} }
const msgDialog = $("#msgDialog"); const msgDialog = jQuery("#msgDialog");
if (msgDialog.length) if (msgDialog.length)
{ {
msgDialog.dialog("destroy").remove(); msgDialog.dialog("destroy").remove();
@ -459,7 +461,7 @@ export class GUI
this._estimateDialogScalingFactor(); this._estimateDialogScalingFactor();
const dialogSize = this._getDialogSize(); const dialogSize = this._getDialogSize();
const self = this; const self = this;
$("#msgDialog").dialog({ jQuery("#msgDialog").dialog({
dialogClass: 'no-close', dialogClass: 'no-close',
width: dialogSize[0], width: dialogSize[0],
@ -476,7 +478,7 @@ export class GUI
text: "Ok", text: "Ok",
click: function () click: function ()
{ {
$(this).dialog("destroy").remove(); jQuery(this).dialog("destroy").remove();
// execute callback function: // execute callback function:
if (typeof onOK !== 'undefined') if (typeof onOK !== 'undefined')
@ -514,10 +516,10 @@ export class GUI
return () => return () =>
{ {
const windowSize = [$(window).width(), $(window).height()]; const windowSize = [jQuery(window).width(), jQuery(window).height()];
// note: $(dialogId) is the dialog-content, $(dialogId).parent() is the actual widget // note: jQuery(dialogId) is the dialog-content, jQuery(dialogId).parent() is the actual widget
const parent = $(dialogId).parent(); const parent = jQuery(dialogId).parent();
parent.css({ parent.css({
position: 'absolute', position: 'absolute',
left: Math.max(0, (windowSize[0] - parent.outerWidth()) / 2.0), left: Math.max(0, (windowSize[0] - parent.outerWidth()) / 2.0),
@ -526,8 +528,8 @@ export class GUI
// record width and height difference between dialog content and dialog: // record width and height difference between dialog content and dialog:
self._contentDelta = [ self._contentDelta = [
parent.css('width').slice(0, -2) - $(dialogId).css('width').slice(0, -2), parent.css('width').slice(0, -2) - jQuery(dialogId).css('width').slice(0, -2),
parent.css('height').slice(0, -2) - $(dialogId).css('height').slice(0, -2)]; parent.css('height').slice(0, -2) - jQuery(dialogId).css('height').slice(0, -2)];
}; };
} }
@ -544,10 +546,10 @@ export class GUI
{ {
const self = this; const self = this;
$(window).resize(function () jQuery(window).resize(function ()
{ {
const parent = $(dialogId).parent(); const parent = jQuery(dialogId).parent();
const windowSize = [$(window).width(), $(window).height()]; const windowSize = [jQuery(window).width(), jQuery(window).height()];
// size (we need to redimension both the dialog and the dialog content): // size (we need to redimension both the dialog and the dialog content):
const dialogSize = self._getDialogSize(); const dialogSize = self._getDialogSize();
@ -559,7 +561,7 @@ export class GUI
const isDifferent = self._estimateDialogScalingFactor(); const isDifferent = self._estimateDialogScalingFactor();
if (!isDifferent) if (!isDifferent)
{ {
$(dialogId).css({ jQuery(dialogId).css({
width: dialogSize[0] - self._contentDelta[0], width: dialogSize[0] - self._contentDelta[0],
maxHeight: dialogSize[1] - self._contentDelta[1] maxHeight: dialogSize[1] - self._contentDelta[1]
}); });
@ -592,7 +594,7 @@ export class GUI
{ {
// for each resource, we have a 'downloading resource' and a 'resource downloaded' message: // for each resource, we have a 'downloading resource' and a 'resource downloaded' message:
this._progressBarMax = signal.count * 2; this._progressBarMax = signal.count * 2;
$("#progressbar").progressbar("option", "max", this._progressBarMax); jQuery("#progressbar").progressbar("option", "max", this._progressBarMax);
this._progressBarCurrentValue = 0; this._progressBarCurrentValue = 0;
} }
@ -601,7 +603,7 @@ export class GUI
else if (signal.message === ServerManager.Event.DOWNLOAD_COMPLETED) else if (signal.message === ServerManager.Event.DOWNLOAD_COMPLETED)
{ {
this._allResourcesDownloaded = true; this._allResourcesDownloaded = true;
$("#progressMsg").text('all resources downloaded.'); jQuery("#progressMsg").text('all resources downloaded.');
this._updateOkButtonStatus(); this._updateOkButtonStatus();
} }
@ -617,20 +619,20 @@ export class GUI
if (signal.message === ServerManager.Event.RESOURCE_DOWNLOADED) if (signal.message === ServerManager.Event.RESOURCE_DOWNLOADED)
{ {
$("#progressMsg").text('downloaded ' + (this._progressBarCurrentValue / 2) + ' / ' + (this._progressBarMax / 2)); jQuery("#progressMsg").text('downloaded ' + (this._progressBarCurrentValue / 2) + ' / ' + (this._progressBarMax / 2));
} }
else else
{ {
$("#progressMsg").text('downloading ' + (this._progressBarCurrentValue / 2) + ' / ' + (this._progressBarMax / 2)); jQuery("#progressMsg").text('downloading ' + (this._progressBarCurrentValue / 2) + ' / ' + (this._progressBarMax / 2));
} }
// $("#progressMsg").text(signal.resource + ': downloaded.'); // $("#progressMsg").text(signal.resource + ': downloaded.');
$("#progressbar").progressbar("option", "value", this._progressBarCurrentValue); jQuery("#progressbar").progressbar("option", "value", this._progressBarCurrentValue);
} }
// unknown message: we just display it // unknown message: we just display it
else else
{ {
$("#progressMsg").text(signal.message); jQuery("#progressMsg").text(signal.message);
} }
} }
@ -649,23 +651,23 @@ export class GUI
{ {
if (changeFocus) if (changeFocus)
{ {
$("#buttonOk").button("option", "disabled", false).focus(); jQuery("#buttonOk").button("option", "disabled", false).focus();
} }
else else
{ {
$("#buttonOk").button("option", "disabled", false); jQuery("#buttonOk").button("option", "disabled", false);
} }
} }
else else
{ {
$("#buttonOk").button("option", "disabled", true); jQuery("#buttonOk").button("option", "disabled", true);
} }
// strangely, changing the disabled option sometimes fails to update the ui, // strangely, changing the disabled option sometimes fails to update the ui,
// so we need to hide it and show it again: // so we need to hide it and show it again:
$("#buttonOk").hide(0, () => jQuery("#buttonOk").hide(0, () =>
{ {
$("#buttonOk").show(); jQuery("#buttonOk").show();
}); });
} }
@ -680,7 +682,7 @@ export class GUI
*/ */
_estimateDialogScalingFactor() _estimateDialogScalingFactor()
{ {
const windowSize = [$(window).width(), $(window).height()]; const windowSize = [jQuery(window).width(), jQuery(window).height()];
// desktop: // desktop:
let dialogScalingFactor = 1.0; let dialogScalingFactor = 1.0;
@ -715,7 +717,7 @@ export class GUI
*/ */
_getDialogSize() _getDialogSize()
{ {
const windowSize = [$(window).width(), $(window).height()]; const windowSize = [jQuery(window).width(), jQuery(window).height()];
this._estimateDialogScalingFactor(); this._estimateDialogScalingFactor();
return [ return [

View File

@ -8,11 +8,12 @@
*/ */
import log4javascript from 'log4javascript';
import pako from 'pako';
import * as util from '../util/Util'; import * as util from '../util/Util';
import {MonotonicClock} from '../util/Clock'; import {MonotonicClock} from '../util/Clock';
import {ExperimentHandler} from '../data/ExperimentHandler'; import {ExperimentHandler} from '../data/ExperimentHandler';
/** /**
* <p>This class handles a variety of loggers, e.g. a browser console one (mostly for debugging), * <p>This class handles a variety of loggers, e.g. a browser console one (mostly for debugging),
* a remote one, etc.</p> * a remote one, etc.</p>

View File

@ -8,7 +8,7 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import log4javascript from 'log4javascript';
import {Scheduler} from '../util/Scheduler'; import {Scheduler} from '../util/Scheduler';
import {ServerManager} from './ServerManager'; import {ServerManager} from './ServerManager';
import {ExperimentHandler} from '../data/ExperimentHandler'; import {ExperimentHandler} from '../data/ExperimentHandler';
@ -182,7 +182,7 @@ export class PsychoJS
this.logger.info('[PsychoJS] @version 2021.1.4'); this.logger.info('[PsychoJS] @version 2021.1.4');
// Hide #root::after // Hide #root::after
$('#root').addClass('is-ready'); jQuery('#root').addClass('is-ready');
} }
@ -696,7 +696,7 @@ export class PsychoJS
this._IP = {}; this._IP = {};
try try
{ {
const geoResponse = await $.get('http://www.geoplugin.net/json.gp'); const geoResponse = await jQuery.get('http://www.geoplugin.net/json.gp');
const geoData = JSON.parse(geoResponse); const geoData = JSON.parse(geoResponse);
this._IP = { this._IP = {
IP: geoData.geoplugin_request, IP: geoData.geoplugin_request,

View File

@ -7,15 +7,14 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import createjs from 'preload-js';
import { Howl } from 'howler';
import {PsychoJS} from './PsychoJS'; import {PsychoJS} from './PsychoJS';
import {PsychObject} from '../util/PsychObject'; import {PsychObject} from '../util/PsychObject';
import * as util from '../util/Util'; import * as util from '../util/Util';
import {ExperimentHandler} from "../data/ExperimentHandler"; import {ExperimentHandler} from "../data/ExperimentHandler";
import {MonotonicClock} from "../util/Clock"; import {MonotonicClock} from "../util/Clock";
// import { Howl } from 'howler';
/** /**
* <p>This manager handles all communications between the experiment running in the participant's browser and the [pavlovia.org]{@link http://pavlovia.org} server, <em>in an asynchronous manner</em>.</p> * <p>This manager handles all communications between the experiment running in the participant's browser and the [pavlovia.org]{@link http://pavlovia.org} server, <em>in an asynchronous manner</em>.</p>
@ -87,7 +86,7 @@ export class ServerManager extends PsychObject
const self = this; const self = this;
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
$.get(configURL, 'json') jQuery.get(configURL, 'json')
.done((config, textStatus) => .done((config, textStatus) =>
{ {
// resolve({ ...response, config }); // resolve({ ...response, config });
@ -144,7 +143,7 @@ export class ServerManager extends PsychObject
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
const url = this._psychoJS.config.pavlovia.URL + '/api/v2/experiments/' + encodeURIComponent(self._psychoJS.config.experiment.fullpath) + '/sessions'; const url = this._psychoJS.config.pavlovia.URL + '/api/v2/experiments/' + encodeURIComponent(self._psychoJS.config.experiment.fullpath) + '/sessions';
$.post(url, data, null, 'json') jQuery.post(url, data, null, 'json')
.done((data, textStatus) => .done((data, textStatus) =>
{ {
if (!('token' in data)) if (!('token' in data))
@ -246,7 +245,7 @@ export class ServerManager extends PsychObject
const self = this; const self = this;
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
$.ajax({ jQuery.ajax({
url, url,
type: 'delete', type: 'delete',
data: {isCompleted}, data: {isCompleted},
@ -669,7 +668,7 @@ export class ServerManager extends PsychObject
value value
}; };
$.post(url, data, null, 'json') jQuery.post(url, data, null, 'json')
.done((serverData, textStatus) => .done((serverData, textStatus) =>
{ {
self.setStatus(ServerManager.Status.READY); self.setStatus(ServerManager.Status.READY);
@ -731,7 +730,7 @@ export class ServerManager extends PsychObject
'/sessions/' + self._psychoJS.config.session.token + '/sessions/' + self._psychoJS.config.session.token +
'/logs'; '/logs';
$.post(url, data, null, 'json') jQuery.post(url, data, null, 'json')
.done((serverData, textStatus) => .done((serverData, textStatus) =>
{ {
self.setStatus(ServerManager.Status.READY); self.setStatus(ServerManager.Status.READY);
@ -860,7 +859,7 @@ export class ServerManager extends PsychObject
'/api/v2/experiments/' + encodeURIComponent(this._psychoJS.config.experiment.fullpath) + '/api/v2/experiments/' + encodeURIComponent(this._psychoJS.config.experiment.fullpath) +
'/resources'; '/resources';
$.get(url, data, null, 'json') jQuery.get(url, data, null, 'json')
.done((data, textStatus) => .done((data, textStatus) =>
{ {
if (!('resources' in data)) if (!('resources' in data))

View File

@ -7,6 +7,7 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import * as PIXI from 'pixi.js-legacy';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {PsychObject} from '../util/PsychObject'; import {PsychObject} from '../util/PsychObject';
import {MonotonicClock} from '../util/Clock'; import {MonotonicClock} from '../util/Clock';

View File

@ -8,6 +8,7 @@
*/ */
import * as XLSX from 'xlsx';
import {PsychObject} from '../util/PsychObject'; import {PsychObject} from '../util/PsychObject';
import {MonotonicClock} from '../util/Clock'; import {MonotonicClock} from '../util/Clock';
import * as util from '../util/Util'; import * as util from '../util/Util';

View File

@ -10,10 +10,11 @@
*/ */
import seedrandom from 'seedrandom';
import * as XLSX from 'xlsx';
import {PsychObject} from '../util/PsychObject'; import {PsychObject} from '../util/PsychObject';
import * as util from '../util/Util'; import * as util from '../util/Util';
/** /**
* <p>A Trial Handler handles the importing and sequencing of conditions.</p> * <p>A Trial Handler handles the importing and sequencing of conditions.</p>
* *
@ -618,11 +619,11 @@ export class TrialHandler extends PsychObject
// seed the random number generator: // seed the random number generator:
if (typeof (this.seed) !== 'undefined') if (typeof (this.seed) !== 'undefined')
{ {
Math.seedrandom(this.seed); seedrandom(this.seed);
} }
else else
{ {
Math.seedrandom(); seedrandom();
} }
if (this.method === TrialHandler.Method.SEQUENTIAL) if (this.method === TrialHandler.Method.SEQUENTIAL)

View File

@ -7,6 +7,7 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import * as Tone from 'tone';
import {SoundPlayer} from './SoundPlayer'; import {SoundPlayer} from './SoundPlayer';
@ -243,7 +244,7 @@ export class TonePlayer extends SoundPlayer
playToneCallback, playToneCallback,
this.duration_s, this.duration_s,
Tone.now(), Tone.now(),
Tone.Infinity Infinity
); );
} }
else else

View File

@ -7,6 +7,8 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import moment from 'moment';
/** /**
* <p>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.</p> * <p>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.</p>

View File

@ -7,6 +7,8 @@
* @license Distributed under the terms of the MIT License * @license Distributed under the terms of the MIT License
*/ */
import * as PIXI from 'pixi.js-legacy';
/** /**
* Syntactic sugar for Mixins * Syntactic sugar for Mixins

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';
import * as util from '../util/Util'; import * as util from '../util/Util';
@ -42,6 +43,7 @@ import {Slider} from './Slider';
* @param {number[]} [options.items= []] - the array of labels * @param {number[]} [options.items= []] - the array of labels
* @param {number} [options.itemPadding= 0.05] - the granularity * @param {number} [options.itemPadding= 0.05] - the granularity
* *
* @param {string} [options.font= 'Arial'] - the text font
* @param {string} [options.fontFamily= 'Helvetica'] - the text font * @param {string} [options.fontFamily= 'Helvetica'] - the text font
* @param {boolean} [options.bold= true] - whether or not the font of the labels is bold * @param {boolean} [options.bold= true] - whether or not the font of the labels is bold
* @param {boolean} [options.italic= false] - whether or not the font of the labels is italic * @param {boolean} [options.italic= false] - whether or not the font of the labels is italic
@ -55,7 +57,7 @@ import {Slider} from './Slider';
*/ */
export class Form extends util.mix(VisualStim).with(ColorMixin) 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, 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});
@ -118,6 +120,13 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
); );
// fonts: // fonts:
this._addAttribute(
'font',
font,
'Arial',
this._onChange(true, true)
);
// Not in use at present
this._addAttribute( this._addAttribute(
'fontFamily', 'fontFamily',
fontFamily, fontFamily,
@ -725,7 +734,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const textStimOption = { const textStimOption = {
win: this._win, win: this._win,
name: 'item text', name: 'item text',
font: 'Arial', font: this.font,
units: this._units, units: this._units,
alignHoriz: 'left', alignHoriz: 'left',
alignVert: 'top', alignVert: 'top',
@ -741,7 +750,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
name: 'choice response', name: 'choice response',
units: this._units, units: this._units,
flip: false, flip: false,
fontFamily: 'Arial', // Not part of Slider options as things stand
fontFamily: this.fontFamily,
// As found in Slider options
font: this.font,
bold: false, bold: false,
italic: false, italic: false,
fontSize: this._fontSize * this._responseTextHeightRatio, fontSize: this._fontSize * this._responseTextHeightRatio,
@ -760,7 +772,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
flip: false, flip: false,
opacity: 1, opacity: 1,
depth: this._depth + 1, depth: this._depth + 1,
font: 'Arial', font: this.font,
letterHeight: this._fontSize * this._responseTextHeightRatio, letterHeight: this._fontSize * this._responseTextHeightRatio,
bold: false, bold: false,
italic: false, italic: false,

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';

View File

@ -9,6 +9,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';
@ -44,7 +45,7 @@ import {PsychoJS} from "../core/PsychoJS";
* and labels with respect to the central bar * and labels with respect to the central bar
* @param {boolean} [options.readOnly= false] - whether or not the slider is read only * @param {boolean} [options.readOnly= false] - whether or not the slider is read only
* *
* @param {string} [options.fontFamily= 'Helvetica'] - the text font * @param {string} [options.font= 'Arial'] - the text font
* @param {boolean} [options.bold= true] - whether or not the font of the labels is bold * @param {boolean} [options.bold= true] - whether or not the font of the labels is bold
* @param {boolean} [options.italic= false] - whether or not the font of the labels is italic * @param {boolean} [options.italic= false] - whether or not the font of the labels is italic
* @param {number} [options.fontSize] - the font size of the labels (in pixels), the default fontSize depends on the * @param {number} [options.fontSize] - the font size of the labels (in pixels), the default fontSize depends on the
@ -130,7 +131,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._addAttribute( this._addAttribute(
'font', 'font',
font, font,
'Helvetica', 'Arial',
this._onChange(true, true) this._onChange(true, true)
); );
this._addAttribute( this._addAttribute(

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';
@ -59,6 +60,12 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
'', '',
this._onChange(true, true) this._onChange(true, true)
); );
this._addAttribute(
'placeholder',
text,
'',
this._onChange(true, true)
);
this._addAttribute( this._addAttribute(
'anchor', 'anchor',
anchor, anchor,
@ -404,6 +411,9 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
{ {
this._pixi.destroy(true); this._pixi.destroy(true);
} }
// Get the currently entered text
let enteredText = this._pixi !== undefined? this._pixi.text: '';
// Create new TextInput
this._pixi = new TextInput(this._getTextInputOptions()); this._pixi = new TextInput(this._getTextInputOptions());
// listeners required for regular textboxes, but may cause problems with button stimuli // listeners required for regular textboxes, but may cause problems with button stimuli
@ -425,7 +435,8 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
} }
if (this._editable) if (this._editable)
{ {
this._pixi.placeholder = this._text; this.text = enteredText;
this._pixi.placeholder = this._placeholder;
} }
else else
{ {

View File

@ -9,6 +9,8 @@
* We are currently using it almost as is but will be making modification in the near future. * We are currently using it almost as is but will be making modification in the near future.
*/ */
import * as PIXI from 'pixi.js-legacy';
export class TextInput extends PIXI.Container export class TextInput extends PIXI.Container
{ {
constructor(styles) constructor(styles)

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {VisualStim} from './VisualStim'; import {VisualStim} from './VisualStim';
import {Color} from '../util/Color'; import {Color} from '../util/Color';
import {ColorMixin} from '../util/ColorMixin'; import {ColorMixin} from '../util/ColorMixin';

View File

@ -8,6 +8,7 @@
*/ */
import * as PIXI from 'pixi.js-legacy';
import {MinimalStim} from '../core/MinimalStim'; import {MinimalStim} from '../core/MinimalStim';
import {WindowMixin} from '../core/WindowMixin'; import {WindowMixin} from '../core/WindowMixin';
import * as util from '../util/Util'; import * as util from '../util/Util';