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

Merge pull request #1 from psychopy/main

fetch from upstream, incorporate recent Sotiri changes
This commit is contained in:
Alain Pitiot 2021-05-31 08:48:29 +02:00 committed by GitHub
commit 0215d01c54
19 changed files with 10133 additions and 48 deletions

View File

@ -0,0 +1,200 @@
# Starts up a separate BrowserStack session for each combination of platform and test.
# That's relatively slow, but also thorough, since a failed test does not abort any successive tests.
name: Automated Test (full)
on:
schedule:
- cron: '0 0 * * sat'
workflow_dispatch:
inputs:
repo:
description: 'PsychoJS repo (tpronk/psychojs)'
required: false
ref:
description: 'branch/tag/SHA (default branch)'
required: false
testrun:
description: 'testrun (full)'
required: false
subset:
description: 'subset (false)'
required: false
platform:
description: 'platform (\*)'
required: false
label:
description: 'label (full)'
required: false
env:
BROWSERSTACK_ACCESSKEY: ${{ secrets.BROWSERSTACK_ACCESSKEY }}
BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }}
STAGING_PASSWORD: ${{ secrets.STAGING_PASSWORD }}
STAGING_PORT: ${{ secrets.STAGING_PORT }}
STAGING_USERNAME: ${{ secrets.STAGING_USERNAME }}
PSYCHOJS_PATH: '/home/runner/work/psychojs/psychojs/psychojs'
DEFAULT_REPO: 'tpronk/psychojs'
DEFAULT_TESTRUN: 'full'
DEFAULT_SUBSET: 'false'
DEFAULT_PLATFORM: '\*'
DEFAULT_LABEL: 'full'
jobs:
prepare:
name: Clean up and get tests
runs-on: ubuntu-latest
outputs:
tests: ${{steps.get_tests.outputs.tests}}
steps:
# START: install psychojs_testing
- name: Checkout psychojs_testing
uses: actions/checkout@v2
with:
repository: psychopy/psychojs_testing
path: psychojs_testing
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache modules psychojs_testing
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs_testing
with:
path: ~/psychojs_testing/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs_testing
run: npm --prefix psychojs_testing ci
# END: install psychojs_testing
- name: Turnstyle (wait for other workflows to complete)
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Sync BS logs and Stager with GitHub
run: node scripts/cli/syncWithGitHub.cjs alain sotiri thomas
working-directory: ./psychojs_testing
- name: Delete old test logs
run: node scripts/cli/deleteLogs.cjs --testrun ${{github.event.inputs.testrun || env.DEFAULT_TESTRUN}}
working-directory: ./psychojs_testing
- name: Get tests
id: get_tests
run: echo "::set-output name=tests::$(node scripts/cli/tabulateTests.cjs --label ${{github.event.inputs.label || env.DEFAULT_LABEL}} --outputFormat paths)"
working-directory: ./psychojs_testing
tests:
name: Test
needs: [prepare]
runs-on: ubuntu-latest
continue-on-error: true
strategy:
max-parallel: 1
matrix:
test: ${{fromJson(needs.prepare.outputs.tests)}}
steps:
# START: install psychojs_testing
- name: Checkout psychojs_testing
uses: actions/checkout@v2
with:
repository: psychopy/psychojs_testing
path: psychojs_testing
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache modules psychojs_testing
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs_testing
with:
path: ~/psychojs_testing/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs_testing
run: npm --prefix psychojs_testing ci
# END: install psychojs_testing
# START: install psychojs
- name: Checkout psychojs
uses: actions/checkout@v2
with:
repository: ${{github.event.inputs.repo || env.DEFAULT_REPO}}
ref: ${{github.event.inputs.ref}}
path: psychojs
- name: Cache modules psychojs
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs
with:
path: ~/psychojs/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs
run: npm --prefix psychojs ci
# END: install psychojs
# START: wait for other workflows to complete
- name: Turnstyle (wait for other workflows to complete)
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# END: wait for other workflows to complete
# START: run tests
- name: Run test
run: node test.cjs --server bs --testrun ${{github.event.inputs.testrun || env.DEFAULT_TESTRUN}} --subset ${{github.event.inputs.subset || env.DEFAULT_SUBSET}} --platform ${{github.event.inputs.platform || env.DEFAULT_PLATFORM}} --url stager --uploadExperiments --uploadResults --label ${{matrix.test}}
working-directory: ./psychojs_testing
# END: run tests
join_reports:
name: Join reports
needs: [tests]
runs-on: ubuntu-latest
continue-on-error: true
steps:
# START: install psychojs_testing
- name: Checkout psychojs_testing
uses: actions/checkout@v2
with:
repository: psychopy/psychojs_testing
path: psychojs_testing
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache modules psychojs_testing
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs_testing
with:
path: ~/psychojs_testing/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs_testing
run: npm --prefix psychojs_testing ci
# END: install psychojs_testing
- name: Turnstyle (wait for other workflows to complete)
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Join reports
run: node scripts/cli/joinReports.cjs --testrun ${{github.event.inputs.testrun || env.DEFAULT_TESTRUN}}
working-directory: ./psychojs_testing

View File

@ -0,0 +1,106 @@
# Starts up one BrowserStack session per platform, then runs each test in succession.
# That's relatively fast, but less thorough, since a failed test aborts any successive tests.
name: Automated Test (short)
on:
# push:
# branches: '**'
# pull_request:
# branches: '**'
workflow_dispatch:
inputs:
repo:
description: 'PsychoJS repo (psychopy/psychojs)'
required: false
ref:
description: 'branch/tag/SHA (default branch)'
required: false
testrun:
description: 'testrun (short)'
required: false
subset:
description: 'subset (true)'
required: false
platform:
description: 'platform (\*)'
required: false
label:
description: 'label (short)'
required: false
env:
BROWSERSTACK_ACCESSKEY: ${{ secrets.BROWSERSTACK_ACCESSKEY }}
BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }}
STAGING_PASSWORD: ${{ secrets.STAGING_PASSWORD }}
STAGING_PORT: ${{ secrets.STAGING_PORT }}
STAGING_USERNAME: ${{ secrets.STAGING_USERNAME }}
PSYCHOJS_PATH: '/home/runner/work/psychojs/psychojs/psychojs'
DEFAULT_REPO: 'psychopy/psychojs'
DEFAULT_TESTRUN: 'short'
DEFAULT_SUBSET: 'true'
DEFAULT_PLATFORM: '\*'
DEFAULT_LABEL: 'short'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Setup node
uses: actions/setup-node@v1
with:
node-version: '12'
# START: install psychojs_testing
- name: Checkout psychojs_testing
uses: actions/checkout@v2
with:
repository: psychopy/psychojs_testing
path: psychojs_testing
- name: Cache modules psychojs_testing
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs_testing
with:
path: ~/psychojs_testing/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs_testing
run: npm --prefix psychojs_testing ci
# END: install psychojs_testing
# START: install psychojs
- name: Checkout psychojs
uses: actions/checkout@v2
with:
repository: ${{github.event.inputs.repo || env.DEFAULT_REPO}}
ref: ${{github.event.inputs.ref}}
path: psychojs
- name: Cache modules psychojs
uses: actions/cache@v2
env:
cache-name: cache-modules-psychojs
with:
path: ~/psychojs/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-:
- name: Install psychojs
run: npm --prefix psychojs ci
# END: install psychojs
# START: wait for other workflows to complete
- name: Turnstyle (wait for other workflows to complete)
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# END: wait for other workflows to complete
# START: run tests
- name: Run tests
run: node test.cjs --server bs --testrun ${{github.event.inputs.testrun || env.DEFAULT_TESTRUN}} --subset ${{github.event.inputs.subset || env.DEFAULT_SUBSET}} --platform ${{github.event.inputs.platform || env.DEFAULT_PLATFORM}} --url stager --uploadExperiments --uploadResults --label ${{github.event.inputs.label || env.DEFAULT_LABEL}}
working-directory: ./psychojs_testing
# END: run tests

1
.gitignore vendored
View File

@ -1 +1,2 @@
dist
node_modules

30
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,30 @@
# Have you ever contributed to an Open Source project?
Your first contribution can be a bit intimidating, but feel free to give it a try. If you get stuck, don't hesitate to ask for help in our [developer forum](https://discourse.psychopy.org/c/dev). This is also a good place to pitch your idea. Next up:
* **I won't program it myself.** Please file a [GitHub issue](https://github.com/psychopy/psychojs/issues).
* **I'd like to take a shot.** Read on to find out how!
# How to contribute
Contributing to PsychoJS consists of four steps:
1. Getting your own copy
2. Making your changes
3. Committing your changes
4. Submitting a Pull Request
## 1. Getting your own copy of the PsychoJS codebase
To be sure your improvements can easily be integrated, follow these steps:
1. **Make a [fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) of the [PsychoJS repo](https://github.com/psychopy/psychojs).** This provides you with your own copy of the PsychoJS source code.
2. **Inside your fork, make a new [branch](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-branches) for the feature you've got in mind.** Base your new branch on the *main* branch. We tend to name branches after the feature we're building. For example `olfactory_component`.
3. **Clone your fork to your hard drive.** Next, switch to the new branch and you're all set up!
## 2. Making your changes
To help you get started with modifying PsychoJS, we've a couple of [developer guides](https://psychopy.org/online/index.html). To try out your modified PsychoJS, consider [creating and running some tests](https://github.com/psychopy/psychojs_testing).
## 3. Committing your changes
Once you're happy with your changes, commit them to your GitHub repo. Please use the tags below in your commit and add an informative message.
- **BF:** bug fix
- **RF:** refactoring
- **ENH:** enhancement (such as a new feature)
- **DOC:** for all kinds of documentation related commits
## 4. File a Pull Request
Once you're done, it's time to add it to the central PsychoJS source code. File a [Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) from your own fork and branch to the *main* branch in the PsychoJS repo. Thanks for contributing!

View File

@ -1,8 +1,14 @@
# PsychoJS
PsychoJS is a JavaScript library that makes it possible to run neuroscience, psychology, and psychophysics experiments in a browser. It is the online counterpart of the [PsychoPy](http://www.psychopy.org/) Python library.
It is also a git submodule: [psychopy/psychojs](https://github.com/psychopy/psychojs)
[![Automated Test (short)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(short).yml/badge.svg)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(short).yml)
[![Automated Test (full)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(full).yml/badge.svg)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(full).yml)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](code-of-conduct.md)
PsychoJS is a JavaScript library that makes it possible to run neuroscience, psychology, and psychophysics experiments in a browser. It is the online counterpart of the [PsychoPy](http://www.psychopy.org/) Python library.
You can create online experiments from the [PsychoPy Builder](http://www.psychopy.org/builder/builder.html), you can find and adapt existing experiments on [pavlovia.org](https://www.pavlovia.org), or create them from scratch: the PsychoJS API is available [here](https://psychopy.github.io/psychojs/).
PsychoJS is an open-source project. You can contribute by submitting pull requests to the [PsychoJS GitHub repository](https://github.com/psychopy/psychojs), and discuss issues and current and future features on the [Online category of the PsychoPy Forum](https://discourse.psychopy.org/c/online).
## Motivation
@ -18,7 +24,7 @@ The idea behind PsychoJS is to make PsychoPy experiments available online, from
Running PsychoPy experiments online requires the generation of an index.html file and of a javascript file that contains the code describing the experiment. Those files need to be hosted on a web server to which participants will point their browser in order to run the experiment. The server will also need to host the PsychoJS library.
### PsychoPy Builder
Starting with PsychoPy version 3.0, [PsychoPy Builder](http://www.psychopy.org/builder/builder.html) can automatically generate the javascript and html files. Many of the existing Builder experiments should "just work", subject to the Components being currently supported by PsychoJS (see below).
The recommended approach to creating experiments is to use [PsychoPy Builder](http://www.psychopy.org/builder/builder.html) to generate the javascript and html files. Many of the existing Builder experiments should "just work", subject to the [Components being compatible between PsychoPy and PsychoJS](https://www.psychopy.org/online/status.html).
### JavaScript Code
We built the PsychoJS library to make the JavaScript experiment files look and behave in very much the same way as to the Builder-generated Python files. PsychoJS offers classes such as `Window` and `ImageStim`, with very similar attributes to their Python equivalents. Experiment designers familiar with the PsychoPy library should feel at home with PsychoJS, and can expect the same level of control they have with PsychoPy, from the structure of the trials/loops all the way down to frame-by-frame updates.
@ -29,32 +35,15 @@ Under the hood PsychoJS relies on [PixiJs](http://www.pixijs.com) to present sti
### Hosting Experiments
A convenient way to make experiment available to participants is to host them on [pavlovia.org](https://www.pavlovia.org), an open-science server under active development. PsychoPy Builder offers the possibility of uploading the experiment directly to pavlovia.org.
A convenient way to make experiment available to participants is to host them on [pavlovia.org](https://www.pavlovia.org), an open-science server. PsychoPy Builder offers the possibility of uploading the experiment directly to pavlovia.org.
## Which PsychoPy Components are supported by PsychoJS?
PsychoJS currently supports the following Components:
### Stimuli:
* Form
* Image
* Rect
* Shape (Polygon)
* Slider
* Sound (tones and tracks)
* Text
* TextBox
* Video
### Events:
* Keyboard
* Mouse
We are constantly adding new Components and are regularly updating this list.
The list of PsychoPy Builder Components supported by PsychoJS see the [PsychoPy/JS online status page](https://www.psychopy.org/online/status.html)
## API
The full documentation of the PsychoJS API is [here](https://psychopy.github.io/psychojs/).
There is full documentation of the [PsychoJS API](https://psychopy.github.io/psychojs/).
## Maintainers

134
code-of-conduct.md Normal file
View File

@ -0,0 +1,134 @@
# Contributor Covenant Code of Conduct
This Code of Conduct applies to all aspects of the community interactions
around PsychoPy; the discourse forum, the GitHub repositories (for PsychoPy
and PsychoJS), our workshops, and any other forms of communication.
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
`admin at opensciencetools.org`.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -15,7 +15,7 @@
<body>
<div id="main">
<div id="main">
<h1 class="page-title">Class: TextInput</h1>
@ -187,4 +187,4 @@
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>
</html>

9439
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
"main": "src/index.js",
"scripts": {
"build": "npm run build:js && npm run build:css && npm run build:docs",
"build:css": "cross-var postcss -o dist/psychojs-$npm_package_version.css src/index.css",
"build:css": "npx cross-var postcss -o dist/psychojs-$npm_package_version.css src/index.css",
"build:docs": "jsdoc src -r -d docs",
"build:js": "rollup -c",
"lint": "npm run lint:js && npm run lint:css",

View File

@ -727,7 +727,17 @@ export class PsychoJS
window.onerror = function (message, source, lineno, colno, error)
{
console.error(error);
self._gui.dialog({error});
document.body.setAttribute('data-error', JSON.stringify({
message: message,
source: source,
lineno: lineno,
colno: colno,
error: error.stack
}));
self._gui.dialog({"error": error});
return true;
};
window.onunhandledrejection = function (error)

View File

@ -302,7 +302,8 @@ export class ExperimentHandler extends PsychObject
// note: we use the XLSX library as it automatically deals with header, takes care of quotes,
// newlines, etc.
const worksheet = XLSX.utils.json_to_sheet(this._trialsData);
const csv = XLSX.utils.sheet_to_csv(worksheet);
// prepend BOM
const csv = '\ufeff' + XLSX.utils.sheet_to_csv(worksheet);
// upload data to the pavlovia server or offer them for download:
const key = __participant + '_' + __experimentName + '_' + __datetime + '.csv';

View File

@ -122,6 +122,17 @@ export class TrialHandler extends PsychObject
}
/**
* Helps go through each trial in the sequence one by one, mirrors PsychoPy.
*/
next() {
const trialIterator = this[Symbol.iterator]();
const { value } = trialIterator.next();
return value;
}
/**
* Iterator over the trial sequence.
*

View File

@ -192,9 +192,6 @@ export class TrackPlayer extends SoundPlayer
}
else
{
self._howl.seek(self._startTime);
self._howl.play();
self._howl.seek(self._startTime);
self._id = self._howl.play();
self._howl.fade(0, self._volume, fadeDuration, self._id);

98
src/visual/ButtonStim.js Normal file
View File

@ -0,0 +1,98 @@
/**
* Button Stimulus.
*
* @author Alain Pitiot
* @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 {TextBox} from './TextBox.js';
import {Mouse} from '../core/Mouse.js';
/**
* <p>ButtonStim visual stimulus.</p>
*
* @name module:visual.ButtonStim
* @class
* @extends TextBox
* @param {Object} options
* @param {module:core.Window} options.win - the associated Window
* @param {String} options.name - the name used when logging messages from this stimulus
* @param {string} [options.text=""] - the text to be rendered
* @param {string} [options.font= "Arial"] - the font family
* @param {Array.<number>} [options.pos= [0, 0]] - the position of the center of the text
* @param {string} [options.anchor= "center"] - horizontal alignment
* @param {string} [options.units= "norm"] - the units of the text size and position
* @param {Color} [options.color= Color("white")] the background color
* @param {Color} [options.fillColor= Color("darkgrey")] the fill color
* @param {Color} [options.borderColor= Color("white")] the border color
* @param {Color} [options.borderWidth= 0] the border width
* @param {number} [options.opacity= 1.0] - the opacity
* @param {number} [options.letterHeight= undefined] - the height of the text
* @param {boolean} [options.bold= true] - whether or not the text is bold
* @param {boolean} [options.italic= false] - whether or not the text is italic
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
* @param {boolean} [options.autoLog= false] - whether or not to log
*/
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} = {})
{
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.listener = new Mouse({name, win, autoLog});
this._addAttribute(
'wasClicked',
false
);
// Arrays to store times of clicks on and off
this._addAttribute(
'timesOn',
[]
);
this._addAttribute(
'timesOff',
[]
);
if (this._autoLog)
{
this._psychoJS.experimentLogger.exp(`Created ${this.name} = ${this.toString()}`);
}
}
/**
* How many times has this button been clicked on?
*
* @name module:visual.ButtonStim#numClicks
* @returns {number} the number of times the button has been clicked on
*/
get numClicks()
{
return this.timesOn.length;
}
/**
* Is this button currently being clicked on?
*
* @name module:visual.ButtonStim#isClicked
* @returns {boolean} whether or not the button is being clicked on
*/
get isClicked()
{
return this.listener.isPressedIn(this, [1, 0, 0]);
}
}

View File

@ -55,7 +55,7 @@ import {Slider} from './Slider';
*/
export class Form extends util.mix(VisualStim).with(ColorMixin)
{
constructor({name, win, pos, size, units, 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, fontFamily, bold, italic, fontSize, clipMask, autoDraw, autoLog} = {})
{
super({name, win, units, opacity, depth, pos, size, clipMask, autoDraw, autoLog});
@ -69,10 +69,47 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// colors:
this._addAttribute(
'color',
// Same as itemColor
color,
'white',
undefined,
this._onChange(true, false)
);
this._addAttribute(
'borderColor',
borderColor,
fillColor,
this._onChange(true, false)
);
this._addAttribute(
'fillColor',
fillColor,
undefined,
this._onChange(true, false)
);
this._addAttribute(
'itemColor',
itemColor,
undefined,
this._onChange(true, false)
);
this._addAttribute(
'markerColor',
markerColor,
undefined,
this._onChange(true, false)
);
this._addAttribute(
'responseColor',
responseColor,
undefined,
this._onChange(true, false)
);
this._addAttribute(
'contrast',
contrast,
@ -329,9 +366,20 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// return a copy of this._items:
return this._items.map(item => Object.assign({}, item));
}
/**
* Check if the form is complete.
*
* @name module:visual.Form#formComplete
* @function
* @public
* @return {boolean} - whether there are any remaining incomplete responses.
*/
formComplete()
{
//same as complete but might be used by some experiments before 2020.2
this.getData();
return this._items._complete;
}
/**
* Add the form data to the given experiment.
*
@ -349,7 +397,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
const _doNotSave = [
'itemCtrl', 'responseCtrl',
'itemColor', 'options', 'ticks', 'tickLabels',
'responseWidth', 'responseColor', 'layout'
'responseWidth', 'responseColor', 'layout'
];
for (const item of this.getData())
@ -682,7 +730,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
alignHoriz: 'left',
alignVert: 'top',
height: this._fontSize,
color: 'white',
color: this.itemColor,
ori: 0,
opacity: 1,
depth: this._depth + 1,
@ -697,7 +745,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
bold: false,
italic: false,
fontSize: this._fontSize * this._responseTextHeightRatio,
color: this._color,
color: this.responseColor,
markerColor: this.markerColor,
opacity: 1,
depth: this._depth + 1,
clipMask: this._stimuliClipMask,
@ -716,9 +765,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
bold: false,
italic: false,
alignment: 'left',
color: this._color,
color: this.responseColor,
fillColor: this.fillColor,
contrast: 1.0,
borderColor: this._color,
borderColor: this.responseColor,
borderWidth: 0.002,
padding: 0.01,
editable: true,
@ -866,7 +916,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
win: this._win,
name: 'scrollbar',
units: this._units,
color: this._color,
color: this.itemColor,
depth: this._depth + 1,
pos: [0, 0],
size: [this._scrollbarWidth, this._size[1]],
@ -1037,11 +1087,12 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
// form background:
this._pixi.lineStyle(1, new Color('lightgray').int, this._opacity, 0.5);
this._pixi.lineStyle(1, new Color(this.borderColor).int, this._opacity, 0.5);
// this._decorations.beginFill(this._barFillColor.int, this._opacity);
this._pixi.beginFill(new Color(this.fillColor).int);
this._pixi.drawRect(this._leftEdge_px, this._bottomEdge_px, this._size_px[0], this._size_px[1]);
// this._decorations.endFill();
this._pixi.endFill();
// item decorators:
this._decorations = new PIXI.Graphics();

View File

@ -65,7 +65,7 @@ import {PsychoJS} from "../core/PsychoJS";
*/
export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
{
constructor({name, win, pos, size, ori, units, color, contrast, opacity, style, ticks, labels, granularity, flip, readOnly, font, bold, italic, fontSize, compact, clipMask, autoDraw, autoLog} = {})
constructor({name, win, pos, size, ori, units, color, markerColor, contrast, opacity, style, ticks, labels, granularity, flip, readOnly, font, bold, italic, fontSize, compact, clipMask, autoDraw, autoLog} = {})
{
super({name, win, units, ori, opacity, pos, size, clipMask, autoDraw, autoLog});
@ -165,6 +165,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
'lightgray',
this._onChange(true, false)
);
this._addAttribute(
'markerColor',
markerColor,
'red',
this._onChange(true, false)
);
this._addAttribute(
'contrast',
contrast,
@ -1078,8 +1084,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
this._tickType = Slider.Shape.LINE;
this._tickColor = (!skin.TICK_COLOR) ? new Color(this._color) : skin.TICK_COLOR;
if (this.markerColor === undefined) {
this.markerColor = skin.MARKER_COLOR;
}
// this._markerColor = this.getContrastedColor(this._color, 0.3);
this._markerColor = skin.MARKER_COLOR;
this.markerColor = new Color(this.markerColor);
this._markerType = Slider.Shape.DISC;
this._markerSize = (!this._skin.MARKER_SIZE) ? this._tickSize : this._skin.MARKER_SIZE;

View File

@ -12,6 +12,7 @@ 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';
// TODO finish documenting all options
@ -125,7 +126,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this._addAttribute(
'borderColor',
borderColor,
'white',
this.fillColor,
this._onChange(true, false)
);
this._addAttribute(
@ -404,6 +405,13 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
this._pixi.destroy(true);
}
this._pixi = new TextInput(this._getTextInputOptions());
// listeners required for regular textboxes, but may cause problems with button stimuli
if (!(this instanceof ButtonStim))
{
this._pixi._addListeners();
}
// 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());

View File

@ -56,7 +56,6 @@ export class TextInput extends PIXI.Container
this._createDOMInput();
this.substituteText = true;
this._setState('DEFAULT');
this._addListeners();
}

View File

@ -1,3 +1,4 @@
export * from './ButtonStim.js';
export * from './Form.js';
export * from './ImageStim.js';
export * from './MovieStim.js';