mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-12 16:48:10 +00:00
Merge pull request #1 from psychopy/main
fetch from upstream, incorporate recent Sotiri changes
This commit is contained in:
commit
0215d01c54
200
.github/workflows/Automated Test (full).yml
vendored
Normal file
200
.github/workflows/Automated Test (full).yml
vendored
Normal 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
|
106
.github/workflows/Automated Test (short).yml
vendored
Normal file
106
.github/workflows/Automated Test (short).yml
vendored
Normal 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
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
dist
|
dist
|
||||||
|
node_modules
|
30
CONTRIBUTING.md
Normal file
30
CONTRIBUTING.md
Normal 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!
|
35
README.md
35
README.md
@ -1,8 +1,14 @@
|
|||||||
# PsychoJS
|
# 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.
|
[.yml/badge.svg)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(short).yml)
|
||||||
It is also a git submodule: [psychopy/psychojs](https://github.com/psychopy/psychojs)
|
[.yml/badge.svg)](https://github.com/psychopy/psychojs/actions/workflows/Automated%20Test%20(full).yml)
|
||||||
|
[](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
|
## 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.
|
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
|
### 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
|
### 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.
|
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
|
### 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?
|
## Which PsychoPy Components are supported by PsychoJS?
|
||||||
PsychoJS currently supports the following Components:
|
The list of PsychoPy Builder Components supported by PsychoJS see the [PsychoPy/JS online status page](https://www.psychopy.org/online/status.html)
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
|
|
||||||
## API
|
## 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
|
## Maintainers
|
||||||
|
134
code-of-conduct.md
Normal file
134
code-of-conduct.md
Normal 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.
|
9439
package-lock.json
generated
Normal file
9439
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
|||||||
"main": "src/index.js",
|
"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": "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:docs": "jsdoc src -r -d docs",
|
||||||
"build:js": "rollup -c",
|
"build:js": "rollup -c",
|
||||||
"lint": "npm run lint:js && npm run lint:css",
|
"lint": "npm run lint:js && npm run lint:css",
|
||||||
|
@ -727,7 +727,17 @@ export class PsychoJS
|
|||||||
window.onerror = function (message, source, lineno, colno, error)
|
window.onerror = function (message, source, lineno, colno, error)
|
||||||
{
|
{
|
||||||
console.error(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;
|
return true;
|
||||||
};
|
};
|
||||||
window.onunhandledrejection = function (error)
|
window.onunhandledrejection = function (error)
|
||||||
|
@ -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,
|
// note: we use the XLSX library as it automatically deals with header, takes care of quotes,
|
||||||
// newlines, etc.
|
// newlines, etc.
|
||||||
const worksheet = XLSX.utils.json_to_sheet(this._trialsData);
|
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:
|
// upload data to the pavlovia server or offer them for download:
|
||||||
const key = __participant + '_' + __experimentName + '_' + __datetime + '.csv';
|
const key = __participant + '_' + __experimentName + '_' + __datetime + '.csv';
|
||||||
|
@ -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.
|
* Iterator over the trial sequence.
|
||||||
*
|
*
|
||||||
|
@ -192,9 +192,6 @@ export class TrackPlayer extends SoundPlayer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self._howl.seek(self._startTime);
|
|
||||||
self._howl.play();
|
|
||||||
|
|
||||||
self._howl.seek(self._startTime);
|
self._howl.seek(self._startTime);
|
||||||
self._id = self._howl.play();
|
self._id = self._howl.play();
|
||||||
self._howl.fade(0, self._volume, fadeDuration, self._id);
|
self._howl.fade(0, self._volume, fadeDuration, self._id);
|
||||||
|
98
src/visual/ButtonStim.js
Normal file
98
src/visual/ButtonStim.js
Normal 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -55,7 +55,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, 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});
|
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:
|
// colors:
|
||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
'color',
|
'color',
|
||||||
|
// Same as itemColor
|
||||||
color,
|
color,
|
||||||
'white',
|
undefined,
|
||||||
this._onChange(true, false)
|
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(
|
this._addAttribute(
|
||||||
'contrast',
|
'contrast',
|
||||||
contrast,
|
contrast,
|
||||||
@ -329,9 +366,20 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
// return a copy of this._items:
|
// return a copy of this._items:
|
||||||
return this._items.map(item => Object.assign({}, item));
|
return this._items.map(item => Object.assign({}, item));
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Check if the form is complete.
|
||||||
|
*
|
||||||
|
* @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.
|
* Add the form data to the given experiment.
|
||||||
*
|
*
|
||||||
@ -682,7 +730,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
alignHoriz: 'left',
|
alignHoriz: 'left',
|
||||||
alignVert: 'top',
|
alignVert: 'top',
|
||||||
height: this._fontSize,
|
height: this._fontSize,
|
||||||
color: 'white',
|
color: this.itemColor,
|
||||||
ori: 0,
|
ori: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
depth: this._depth + 1,
|
depth: this._depth + 1,
|
||||||
@ -697,7 +745,8 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
bold: false,
|
bold: false,
|
||||||
italic: false,
|
italic: false,
|
||||||
fontSize: this._fontSize * this._responseTextHeightRatio,
|
fontSize: this._fontSize * this._responseTextHeightRatio,
|
||||||
color: this._color,
|
color: this.responseColor,
|
||||||
|
markerColor: this.markerColor,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
depth: this._depth + 1,
|
depth: this._depth + 1,
|
||||||
clipMask: this._stimuliClipMask,
|
clipMask: this._stimuliClipMask,
|
||||||
@ -716,9 +765,10 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
bold: false,
|
bold: false,
|
||||||
italic: false,
|
italic: false,
|
||||||
alignment: 'left',
|
alignment: 'left',
|
||||||
color: this._color,
|
color: this.responseColor,
|
||||||
|
fillColor: this.fillColor,
|
||||||
contrast: 1.0,
|
contrast: 1.0,
|
||||||
borderColor: this._color,
|
borderColor: this.responseColor,
|
||||||
borderWidth: 0.002,
|
borderWidth: 0.002,
|
||||||
padding: 0.01,
|
padding: 0.01,
|
||||||
editable: true,
|
editable: true,
|
||||||
@ -866,7 +916,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
win: this._win,
|
win: this._win,
|
||||||
name: 'scrollbar',
|
name: 'scrollbar',
|
||||||
units: this._units,
|
units: this._units,
|
||||||
color: this._color,
|
color: this.itemColor,
|
||||||
depth: this._depth + 1,
|
depth: this._depth + 1,
|
||||||
pos: [0, 0],
|
pos: [0, 0],
|
||||||
size: [this._scrollbarWidth, this._size[1]],
|
size: [this._scrollbarWidth, this._size[1]],
|
||||||
@ -1037,11 +1087,12 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
|
|
||||||
|
|
||||||
// form background:
|
// 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._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._pixi.drawRect(this._leftEdge_px, this._bottomEdge_px, this._size_px[0], this._size_px[1]);
|
||||||
// this._decorations.endFill();
|
// this._decorations.endFill();
|
||||||
|
this._pixi.endFill();
|
||||||
|
|
||||||
// item decorators:
|
// item decorators:
|
||||||
this._decorations = new PIXI.Graphics();
|
this._decorations = new PIXI.Graphics();
|
||||||
|
@ -65,7 +65,7 @@ import {PsychoJS} from "../core/PsychoJS";
|
|||||||
*/
|
*/
|
||||||
export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
|
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});
|
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',
|
'lightgray',
|
||||||
this._onChange(true, false)
|
this._onChange(true, false)
|
||||||
);
|
);
|
||||||
|
this._addAttribute(
|
||||||
|
'markerColor',
|
||||||
|
markerColor,
|
||||||
|
'red',
|
||||||
|
this._onChange(true, false)
|
||||||
|
);
|
||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
'contrast',
|
'contrast',
|
||||||
contrast,
|
contrast,
|
||||||
@ -1078,8 +1084,12 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
|
|||||||
this._tickType = Slider.Shape.LINE;
|
this._tickType = Slider.Shape.LINE;
|
||||||
this._tickColor = (!skin.TICK_COLOR) ? new Color(this._color) : skin.TICK_COLOR;
|
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 = this.getContrastedColor(this._color, 0.3);
|
||||||
this._markerColor = skin.MARKER_COLOR;
|
this.markerColor = new Color(this.markerColor);
|
||||||
this._markerType = Slider.Shape.DISC;
|
this._markerType = Slider.Shape.DISC;
|
||||||
this._markerSize = (!this._skin.MARKER_SIZE) ? this._tickSize : this._skin.MARKER_SIZE;
|
this._markerSize = (!this._skin.MARKER_SIZE) ? this._tickSize : this._skin.MARKER_SIZE;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import {VisualStim} from './VisualStim';
|
|||||||
import {Color} from '../util/Color';
|
import {Color} from '../util/Color';
|
||||||
import {ColorMixin} from '../util/ColorMixin';
|
import {ColorMixin} from '../util/ColorMixin';
|
||||||
import {TextInput} from './TextInput';
|
import {TextInput} from './TextInput';
|
||||||
|
import {ButtonStim} from './ButtonStim.js';
|
||||||
import * as util from '../util/Util';
|
import * as util from '../util/Util';
|
||||||
|
|
||||||
// TODO finish documenting all options
|
// TODO finish documenting all options
|
||||||
@ -125,7 +126,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
'borderColor',
|
'borderColor',
|
||||||
borderColor,
|
borderColor,
|
||||||
'white',
|
this.fillColor,
|
||||||
this._onChange(true, false)
|
this._onChange(true, false)
|
||||||
);
|
);
|
||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
@ -404,6 +405,13 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
this._pixi.destroy(true);
|
this._pixi.destroy(true);
|
||||||
}
|
}
|
||||||
this._pixi = new TextInput(this._getTextInputOptions());
|
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
|
// check if other TextBox instances are already in focus
|
||||||
const { _drawList = [] } = this.psychoJS.window;
|
const { _drawList = [] } = this.psychoJS.window;
|
||||||
const otherTextBoxWithFocus = _drawList.some(item => item instanceof TextBox && item._pixi && item._pixi._hasFocus());
|
const otherTextBoxWithFocus = _drawList.some(item => item instanceof TextBox && item._pixi && item._pixi._hasFocus());
|
||||||
|
@ -56,7 +56,6 @@ export class TextInput extends PIXI.Container
|
|||||||
this._createDOMInput();
|
this._createDOMInput();
|
||||||
this.substituteText = true;
|
this.substituteText = true;
|
||||||
this._setState('DEFAULT');
|
this._setState('DEFAULT');
|
||||||
this._addListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './ButtonStim.js';
|
||||||
export * from './Form.js';
|
export * from './Form.js';
|
||||||
export * from './ImageStim.js';
|
export * from './ImageStim.js';
|
||||||
export * from './MovieStim.js';
|
export * from './MovieStim.js';
|
||||||
|
Loading…
Reference in New Issue
Block a user