jspsych 8.2.3 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/css/jspsych.css +20 -0
- package/dist/index.browser.js +57 -4
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +6 -6
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +56 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +56 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.scss +21 -0
- package/src/modules/plugin-api/KeyboardListenerAPI.ts +90 -1
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ var autoBind = require('auto-bind');
|
|
|
4
4
|
var rw = require('random-words');
|
|
5
5
|
var seedrandom = require('seedrandom/lib/alea.js');
|
|
6
6
|
|
|
7
|
-
var version = "8.
|
|
7
|
+
var version = "8.3.0";
|
|
8
8
|
|
|
9
9
|
class ExtensionManager {
|
|
10
10
|
constructor(dependencies, extensionsConfiguration) {
|
|
@@ -557,6 +557,8 @@ class KeyboardListenerAPI {
|
|
|
557
557
|
this.minimumValidRt = minimumValidRt;
|
|
558
558
|
this.listeners = /* @__PURE__ */ new Set();
|
|
559
559
|
this.heldKeys = /* @__PURE__ */ new Set();
|
|
560
|
+
this.keyDownTimestamps = /* @__PURE__ */ new Map();
|
|
561
|
+
this.pendingReleases = /* @__PURE__ */ new Map();
|
|
560
562
|
this.areRootListenersRegistered = false;
|
|
561
563
|
autoBind(this);
|
|
562
564
|
this.registerRootListeners();
|
|
@@ -571,11 +573,16 @@ class KeyboardListenerAPI {
|
|
|
571
573
|
if (rootElement) {
|
|
572
574
|
rootElement.addEventListener("keydown", this.rootKeydownListener);
|
|
573
575
|
rootElement.addEventListener("keyup", this.rootKeyupListener);
|
|
576
|
+
window.addEventListener("blur", this.rootBlurListener);
|
|
574
577
|
this.areRootListenersRegistered = true;
|
|
575
578
|
}
|
|
576
579
|
}
|
|
577
580
|
}
|
|
578
581
|
rootKeydownListener(e) {
|
|
582
|
+
const physicalKey = this.getPhysicalKey(e);
|
|
583
|
+
if (!this.keyDownTimestamps.has(physicalKey)) {
|
|
584
|
+
this.keyDownTimestamps.set(physicalKey, performance.now());
|
|
585
|
+
}
|
|
579
586
|
for (const listener of [...this.listeners]) {
|
|
580
587
|
listener(e);
|
|
581
588
|
}
|
|
@@ -584,9 +591,43 @@ class KeyboardListenerAPI {
|
|
|
584
591
|
toLowerCaseIfInsensitive(string) {
|
|
585
592
|
return this.areResponsesCaseSensitive ? string : string.toLowerCase();
|
|
586
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* identifies physical key of a keyboard event, for matching a keydown event with
|
|
596
|
+
* its corresponding keyup event, in the case of a change of shift state while
|
|
597
|
+
* the key is being held.
|
|
598
|
+
*/
|
|
599
|
+
getPhysicalKey(e) {
|
|
600
|
+
return e.code || this.toLowerCaseIfInsensitive(e.key);
|
|
601
|
+
}
|
|
587
602
|
rootKeyupListener(e) {
|
|
603
|
+
const physicalKey = this.getPhysicalKey(e);
|
|
604
|
+
for (const [listener, pending] of this.pendingReleases) {
|
|
605
|
+
if (pending.code === physicalKey) {
|
|
606
|
+
const pressTimestamp = this.keyDownTimestamps.get(physicalKey);
|
|
607
|
+
const rt_key_duration = pressTimestamp === void 0 ? null : Math.round(performance.now() - pressTimestamp);
|
|
608
|
+
this.pendingReleases.delete(listener);
|
|
609
|
+
pending.callback_function({ key: pending.key, rt: pending.rt, rt_key_duration });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
this.keyDownTimestamps.delete(physicalKey);
|
|
588
613
|
this.heldKeys.delete(this.toLowerCaseIfInsensitive(e.key));
|
|
589
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* When the window loses focus (`blur`), the browser will not deliver the `keyup` events for keys
|
|
617
|
+
* that are currently held, which would otherwise leave stale entries in `heldKeys` and
|
|
618
|
+
* `keyDownTimestamps` (making a key look permanently held) and orphan any pending releases. Treat
|
|
619
|
+
* the blur as a release of every held key: resolve each pending release with
|
|
620
|
+
* `rt_key_duration: null`, since the true hold duration cannot be measured once the release is
|
|
621
|
+
* never observed, then clear the tracked key state.
|
|
622
|
+
*/
|
|
623
|
+
rootBlurListener() {
|
|
624
|
+
for (const [listener, pending] of this.pendingReleases) {
|
|
625
|
+
this.pendingReleases.delete(listener);
|
|
626
|
+
pending.callback_function({ key: pending.key, rt: pending.rt, rt_key_duration: null });
|
|
627
|
+
}
|
|
628
|
+
this.heldKeys.clear();
|
|
629
|
+
this.keyDownTimestamps.clear();
|
|
630
|
+
}
|
|
590
631
|
isResponseValid(validResponses, allowHeldKey, key) {
|
|
591
632
|
if (!allowHeldKey && this.heldKeys.has(key)) {
|
|
592
633
|
return false;
|
|
@@ -607,7 +648,8 @@ class KeyboardListenerAPI {
|
|
|
607
648
|
audio_context,
|
|
608
649
|
audio_context_start_time,
|
|
609
650
|
allow_held_key = false,
|
|
610
|
-
minimum_valid_rt = this.minimumValidRt
|
|
651
|
+
minimum_valid_rt = this.minimumValidRt,
|
|
652
|
+
wait_for_key_release = false
|
|
611
653
|
}) {
|
|
612
654
|
if (rt_method !== "performance" && rt_method !== "audio") {
|
|
613
655
|
console.log(
|
|
@@ -634,7 +676,16 @@ class KeyboardListenerAPI {
|
|
|
634
676
|
if (!persist) {
|
|
635
677
|
this.cancelKeyboardResponse(listener);
|
|
636
678
|
}
|
|
637
|
-
|
|
679
|
+
if (wait_for_key_release) {
|
|
680
|
+
this.pendingReleases.set(listener, {
|
|
681
|
+
code: this.getPhysicalKey(e),
|
|
682
|
+
key: e.key,
|
|
683
|
+
rt,
|
|
684
|
+
callback_function
|
|
685
|
+
});
|
|
686
|
+
} else {
|
|
687
|
+
callback_function({ key: e.key, rt });
|
|
688
|
+
}
|
|
638
689
|
}
|
|
639
690
|
};
|
|
640
691
|
this.listeners.add(listener);
|
|
@@ -642,9 +693,11 @@ class KeyboardListenerAPI {
|
|
|
642
693
|
}
|
|
643
694
|
cancelKeyboardResponse(listener) {
|
|
644
695
|
this.listeners.delete(listener);
|
|
696
|
+
this.pendingReleases.delete(listener);
|
|
645
697
|
}
|
|
646
698
|
cancelAllKeyboardResponses() {
|
|
647
699
|
this.listeners.clear();
|
|
700
|
+
this.pendingReleases.clear();
|
|
648
701
|
}
|
|
649
702
|
compareKeys(key1, key2) {
|
|
650
703
|
if (typeof key1 !== "string" && key1 !== null || typeof key2 !== "string" && key2 !== null) {
|