mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-10 19:20:55 +00:00
basic grabbing of features working
This commit is contained in:
parent
7bbc2436da
commit
448c50788b
17
package-lock.json
generated
17
package-lock.json
generated
@ -5178,6 +5178,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-browser": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg=="
|
||||||
|
},
|
||||||
"node_modules/detect-file": {
|
"node_modules/detect-file": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||||
@ -14616,8 +14621,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/plugin-browser-check": {
|
"packages/plugin-browser-check": {
|
||||||
|
"name": "@jspsych/plugin-browser-check",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-browser": "^5.2.1"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jspsych/config": "^1.0.0",
|
"@jspsych/config": "^1.0.0",
|
||||||
"@jspsych/test-utils": "^1.0.0"
|
"@jspsych/test-utils": "^1.0.0"
|
||||||
@ -16961,7 +16970,8 @@
|
|||||||
"version": "file:packages/plugin-browser-check",
|
"version": "file:packages/plugin-browser-check",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jspsych/config": "^1.0.0",
|
"@jspsych/config": "^1.0.0",
|
||||||
"@jspsych/test-utils": "^1.0.0"
|
"@jspsych/test-utils": "^1.0.0",
|
||||||
|
"detect-browser": "^5.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@jspsych/plugin-call-function": {
|
"@jspsych/plugin-call-function": {
|
||||||
@ -19332,6 +19342,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||||
},
|
},
|
||||||
|
"detect-browser": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg=="
|
||||||
|
},
|
||||||
"detect-file": {
|
"detect-file": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||||
|
@ -39,5 +39,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jspsych/config": "^1.0.0",
|
"@jspsych/config": "^1.0.0",
|
||||||
"@jspsych/test-utils": "^1.0.0"
|
"@jspsych/test-utils": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"detect-browser": "^5.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,61 @@ jest.useFakeTimers();
|
|||||||
|
|
||||||
describe("browser-check", () => {
|
describe("browser-check", () => {
|
||||||
test("contains data on window size", async () => {
|
test("contains data on window size", async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(navigator, "userAgent", "get")
|
||||||
|
.mockReturnValue(
|
||||||
|
"Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"
|
||||||
|
);
|
||||||
|
|
||||||
const { expectFinished, getData } = await startTimeline([
|
const { expectFinished, getData } = await startTimeline([
|
||||||
{
|
{
|
||||||
type: browserCheck,
|
type: browserCheck,
|
||||||
|
skip_features: ["vsync_rate"],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log(getData().values()[0]);
|
await expectFinished();
|
||||||
|
|
||||||
|
expect(getData().values()[0].width).not.toBeUndefined();
|
||||||
|
expect(getData().values()[0].height).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("contains browser data from userAgent", async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(navigator, "userAgent", "get")
|
||||||
|
.mockReturnValue(
|
||||||
|
"Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"
|
||||||
|
);
|
||||||
|
|
||||||
|
const { expectFinished, getData } = await startTimeline([
|
||||||
|
{
|
||||||
|
type: browserCheck,
|
||||||
|
skip_features: ["vsync_rate"],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
await expectFinished();
|
await expectFinished();
|
||||||
|
|
||||||
expect(getData().values()[0].window_width).not.toBeUndefined();
|
expect(getData().values()[0].browser).toBe("chrome");
|
||||||
|
expect(getData().values()[0].browser_version).toBe("18.0.1025");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("contains OS data", async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(navigator, "userAgent", "get")
|
||||||
|
.mockReturnValue(
|
||||||
|
"Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"
|
||||||
|
);
|
||||||
|
|
||||||
|
const { expectFinished, getData } = await startTimeline([
|
||||||
|
{
|
||||||
|
type: browserCheck,
|
||||||
|
skip_features: ["vsync_rate"],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expectFinished();
|
||||||
|
|
||||||
|
expect(getData().values()[0].os).toBe("Android OS");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { detect } from "detect-browser";
|
||||||
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
|
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
|
||||||
|
|
||||||
const info = <const>{
|
const info = <const>{
|
||||||
@ -10,16 +11,33 @@ const info = <const>{
|
|||||||
type: ParameterType.STRING,
|
type: ParameterType.STRING,
|
||||||
array: true,
|
array: true,
|
||||||
default: [
|
default: [
|
||||||
"window_width",
|
"width",
|
||||||
"window_height",
|
"height",
|
||||||
"webaudio",
|
"webaudio",
|
||||||
"browser",
|
"browser",
|
||||||
"browser_version",
|
"browser_version",
|
||||||
"mobile",
|
"mobile",
|
||||||
"os",
|
"os",
|
||||||
"fullscreen",
|
"fullscreen",
|
||||||
|
"vsync_rate",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Any features listed here will be skipped, even if they appear in `features`. Useful for
|
||||||
|
* when you want to run most of the defaults.
|
||||||
|
*/
|
||||||
|
skip_features: {
|
||||||
|
type: ParameterType.STRING,
|
||||||
|
array: true,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The number of animation frames to sample when calculating vsync_rate
|
||||||
|
*/
|
||||||
|
vsync_frame_count: {
|
||||||
|
type: ParameterType.INT,
|
||||||
|
default: 60,
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* List of inclusion criteria
|
* List of inclusion criteria
|
||||||
*/
|
*/
|
||||||
@ -45,51 +63,115 @@ class BrowserCheckPlugin implements JsPsychPlugin<Info> {
|
|||||||
|
|
||||||
constructor(private jsPsych: JsPsych) {}
|
constructor(private jsPsych: JsPsych) {}
|
||||||
|
|
||||||
private featureCheckFunctionsMap = new Map<string, () => any>(
|
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
||||||
|
const featureCheckFunctionsMap = new Map<string, () => any>(
|
||||||
Object.entries({
|
Object.entries({
|
||||||
window_width: () => {
|
width: () => {
|
||||||
return window.innerWidth;
|
return window.innerWidth;
|
||||||
},
|
},
|
||||||
window_height: () => {
|
height: () => {
|
||||||
return window.innerHeight;
|
return window.innerHeight;
|
||||||
},
|
},
|
||||||
webaudio: () => {
|
webaudio: () => {
|
||||||
// @ts-ignore
|
|
||||||
return (
|
return (
|
||||||
window.AudioContext ||
|
window.AudioContext ||
|
||||||
|
// @ts-ignore
|
||||||
window.webkitAudioContext ||
|
window.webkitAudioContext ||
|
||||||
|
// @ts-ignore
|
||||||
window.mozAudioContext ||
|
window.mozAudioContext ||
|
||||||
|
// @ts-ignore
|
||||||
window.oAudioContext ||
|
window.oAudioContext ||
|
||||||
|
// @ts-ignore
|
||||||
window.msAudioContext
|
window.msAudioContext
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
browser: () => {
|
browser: () => {
|
||||||
return "TODO";
|
return detect().name;
|
||||||
},
|
},
|
||||||
browser_version: () => {
|
browser_version: () => {
|
||||||
return "TODO";
|
return detect().version;
|
||||||
},
|
},
|
||||||
mobile: () => {
|
mobile: () => {
|
||||||
return "TODO";
|
return /Mobi/i.test(window.navigator.userAgent);
|
||||||
},
|
},
|
||||||
os: () => {
|
os: () => {
|
||||||
return "TODO";
|
return detect().os;
|
||||||
},
|
},
|
||||||
fullscreen: () => {
|
fullscreen: () => {
|
||||||
return "TODO";
|
return (
|
||||||
|
document.exitFullscreen ||
|
||||||
|
// @ts-expect-error
|
||||||
|
document.webkitExitFullscreen ||
|
||||||
|
// @ts-expect-error
|
||||||
|
document.msExitFullscreen
|
||||||
|
);
|
||||||
|
},
|
||||||
|
vsync_rate: () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let t0 = performance.now();
|
||||||
|
let deltas = [];
|
||||||
|
let framesToRun = trial.vsync_frame_count;
|
||||||
|
const finish = () => {
|
||||||
|
let sum = 0;
|
||||||
|
for (const v of deltas) {
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
resolve(1000.0 / (sum / deltas.length));
|
||||||
|
};
|
||||||
|
const nextFrame = () => {
|
||||||
|
let t1 = performance.now();
|
||||||
|
deltas.push(t1 - t0);
|
||||||
|
t0 = t1;
|
||||||
|
framesToRun--;
|
||||||
|
if (framesToRun > 0) {
|
||||||
|
requestAnimationFrame(nextFrame);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const start = () => {
|
||||||
|
t0 = performance.now();
|
||||||
|
requestAnimationFrame(nextFrame);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(start);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
|
||||||
const feature_data = new Map<string, any>();
|
const feature_data = new Map<string, any>();
|
||||||
for (const feature of trial.features) {
|
const feature_checks: Promise<void>[] = [];
|
||||||
feature_data.set(feature, this.featureCheckFunctionsMap.get(feature)());
|
const features_to_check = trial.features.filter((x) => !trial.skip_features.includes(x));
|
||||||
|
|
||||||
|
for (const feature of features_to_check) {
|
||||||
|
// this allows for feature check functions to be sync or async
|
||||||
|
feature_checks.push(
|
||||||
|
Promise.resolve(featureCheckFunctionsMap.get(feature)())
|
||||||
|
// Promise.resolve(featureCheckFunctionsMap.get(feature)())
|
||||||
|
// .then((feature_val)=>{
|
||||||
|
// feature_data.set(feature, feature_val);
|
||||||
|
// return;
|
||||||
|
// })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Promise.allSettled(feature_checks).then((results) => {
|
||||||
|
for (let i = 0; i < features_to_check.length; i++) {
|
||||||
|
if (results[i].status === "fulfilled") {
|
||||||
|
// @ts-expect-error
|
||||||
|
feature_data.set(features_to_check[i], results[i].value);
|
||||||
|
} else {
|
||||||
|
feature_data.set(features_to_check[i], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end_trial();
|
||||||
|
});
|
||||||
|
|
||||||
|
var end_trial = () => {
|
||||||
const trial_data = { ...Object.fromEntries(feature_data) };
|
const trial_data = { ...Object.fromEntries(feature_data) };
|
||||||
|
|
||||||
this.jsPsych.finishTrial(trial_data);
|
this.jsPsych.finishTrial(trial_data);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// MINIMUM SIZE
|
// MINIMUM SIZE
|
||||||
|
Loading…
Reference in New Issue
Block a user