mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-12 16:48:10 +00:00
Merge branch 'master' of github.com:apitiot/psychojs
This commit is contained in:
commit
b834b68a3d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
dist
|
dist
|
||||||
|
out
|
||||||
node_modules
|
node_modules
|
11737
package-lock.json
generated
11737
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
83
package.json
83
package.json
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
212
rollup.config.js
212
rollup.config.js
@ -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
13
scripts/build.css.cjs
Normal 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
14
scripts/build.js.cjs
Normal 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`
|
||||||
|
});
|
@ -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;">⚠ 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;">⚠ 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 [
|
||||||
|
@ -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>
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -697,7 +697,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,
|
||||||
|
@ -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))
|
||||||
@ -256,7 +255,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},
|
||||||
@ -679,7 +678,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);
|
||||||
@ -741,7 +740,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);
|
||||||
@ -870,7 +869,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))
|
||||||
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
216
src/data/Shelf.js
Normal file
216
src/data/Shelf.js
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/**
|
||||||
|
* Shelf handles persistent key/value pairs, which are stored in the shelf collection on the
|
||||||
|
* server, and accesses in a concurrent fashion.
|
||||||
|
*
|
||||||
|
* @author Alain Pitiot
|
||||||
|
* @version 2021.1.4
|
||||||
|
* @copyright (c) 2021 Open Science Tools Ltd. (https://opensciencetools.org)
|
||||||
|
* @license Distributed under the terms of the MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {PsychObject} from "../util/PsychObject";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Shelf handles persistent key/value pairs, which are stored in the shelf collection on the
|
||||||
|
* server, and accesses in a concurrent fashion</p>
|
||||||
|
*
|
||||||
|
* @name module:data.Shelf
|
||||||
|
* @class
|
||||||
|
* @extends PsychObject
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {module:core.PsychoJS} options.psychoJS - the PsychoJS instance
|
||||||
|
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||||
|
*/
|
||||||
|
export class Shelf extends PsychObject
|
||||||
|
{
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
psychoJS,
|
||||||
|
autoLog = false
|
||||||
|
} = {})
|
||||||
|
{
|
||||||
|
super(psychoJS);
|
||||||
|
|
||||||
|
this._addAttribute('autoLog', autoLog);
|
||||||
|
this._addAttribute('status', Shelf.Status.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
increment()
|
||||||
|
{/*
|
||||||
|
// prepare a PsychoJS component:
|
||||||
|
this._waitForDownloadComponent = {
|
||||||
|
status: PsychoJS.Status.NOT_STARTED,
|
||||||
|
clock: new Clock(),
|
||||||
|
resources: new Set()
|
||||||
|
};
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
const t = self._waitForDownloadComponent.clock.getTime();
|
||||||
|
|
||||||
|
// start the component:
|
||||||
|
if (t >= 0.0 && self._waitForDownloadComponent.status === PsychoJS.Status.NOT_STARTED)
|
||||||
|
{
|
||||||
|
self._waitForDownloadComponent.tStart = t;
|
||||||
|
self._waitForDownloadComponent.status = PsychoJS.Status.STARTED;
|
||||||
|
|
||||||
|
// if resources is an empty array, we consider all registered resources:
|
||||||
|
if (resources.length === 0)
|
||||||
|
{
|
||||||
|
for (const [name, {status, path, data}] of this._resources)
|
||||||
|
{
|
||||||
|
resources.append({ name, path });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only download those resources not already downloaded or downloading:
|
||||||
|
const resourcesToDownload = new Set();
|
||||||
|
for (let {name, path} of resources)
|
||||||
|
{
|
||||||
|
// to deal with potential CORS issues, we use the pavlovia.org proxy for resources
|
||||||
|
// not hosted on pavlovia.org:
|
||||||
|
if ( (path.toLowerCase().indexOf('www.') === 0 ||
|
||||||
|
path.toLowerCase().indexOf('http:') === 0 ||
|
||||||
|
path.toLowerCase().indexOf('https:') === 0) &&
|
||||||
|
(path.indexOf('pavlovia.org') === -1) )
|
||||||
|
{
|
||||||
|
path = 'https://devlovia.org/api/v2/proxy/' + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathStatusData = this._resources.get(name);
|
||||||
|
|
||||||
|
// the resource has not been registered yet:
|
||||||
|
if (typeof pathStatusData === 'undefined')
|
||||||
|
{
|
||||||
|
self._resources.set(name, {
|
||||||
|
status: ServerManager.ResourceStatus.REGISTERED,
|
||||||
|
path,
|
||||||
|
data: undefined
|
||||||
|
});
|
||||||
|
self._waitForDownloadComponent.resources.add(name);
|
||||||
|
resourcesToDownload.add(name);
|
||||||
|
self._psychoJS.logger.debug('registered resource:', name, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the resource has been registered but is not downloaded yet:
|
||||||
|
else if (typeof pathStatusData.status !== ServerManager.ResourceStatus.DOWNLOADED)
|
||||||
|
// else if (typeof pathStatusData.data === 'undefined')
|
||||||
|
{
|
||||||
|
self._waitForDownloadComponent.resources.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the download:
|
||||||
|
self._downloadResources(resourcesToDownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether all resources have been downloaded:
|
||||||
|
for (const name of self._waitForDownloadComponent.resources)
|
||||||
|
{
|
||||||
|
const pathStatusData = this._resources.get(name);
|
||||||
|
|
||||||
|
// the resource has not been downloaded yet: loop this component
|
||||||
|
if (typeof pathStatusData.status !== ServerManager.ResourceStatus.DOWNLOADED)
|
||||||
|
// if (typeof pathStatusData.data === 'undefined')
|
||||||
|
{
|
||||||
|
return Scheduler.Event.FLIP_REPEAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all resources have been downloaded: move to the next component:
|
||||||
|
self._waitForDownloadComponent.status = PsychoJS.Status.FINISHED;
|
||||||
|
return Scheduler.Event.NEXT;
|
||||||
|
};*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the integer counter corresponding to the given key by the given amount.
|
||||||
|
*
|
||||||
|
* @param {string[]} [key = [] ] key as an array of key components
|
||||||
|
* @param {number} [increment = 1] increment
|
||||||
|
* @return {Promise<any>}
|
||||||
|
*/
|
||||||
|
async _increment(key = [], increment = 1)
|
||||||
|
{
|
||||||
|
const response = {
|
||||||
|
origin: 'Shelf.increment',
|
||||||
|
context: 'when incrementing an integer counter'
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this._status = Shelf.Status.BUSY;
|
||||||
|
|
||||||
|
if (!Array.isArray(key) || key.length === 0)
|
||||||
|
{
|
||||||
|
throw 'the key must be a non empty array';
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare the request:
|
||||||
|
const componentList = key.reduce((list, component) => list + '+' + component, '');
|
||||||
|
const url = this._psychoJS.config.pavlovia.URL + '/api/v2/shelf/' + componentList;
|
||||||
|
const data = { increment };
|
||||||
|
|
||||||
|
// query the server:
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'cors', // no-cors, *cors, same-origin
|
||||||
|
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
||||||
|
credentials: 'same-origin', // include, *same-origin, omit
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'session-token': ''
|
||||||
|
},
|
||||||
|
redirect: 'follow', // manual, *follow, error
|
||||||
|
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
// convert the response to json:
|
||||||
|
const document = await response.json();
|
||||||
|
|
||||||
|
// return the updated value:
|
||||||
|
this._status = Shelf.Status.READY;
|
||||||
|
return document['value'];
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
this._status = Shelf.Status.ERROR;
|
||||||
|
throw {...response, error};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shelf status
|
||||||
|
*
|
||||||
|
* @name module:data.Shelf#Status
|
||||||
|
* @enum {Symbol}
|
||||||
|
* @readonly
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
Shelf.Status = {
|
||||||
|
/**
|
||||||
|
* The shelf is ready.
|
||||||
|
*/
|
||||||
|
READY: Symbol.for('READY'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shelf is busy, e.g. storing or retrieving values.
|
||||||
|
*/
|
||||||
|
BUSY: Symbol.for('BUSY'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shelf has encountered an error.
|
||||||
|
*/
|
||||||
|
ERROR: Symbol.for('ERROR')
|
||||||
|
};
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
Loading…
Reference in New Issue
Block a user