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",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||
@ -14616,8 +14621,12 @@
|
||||
}
|
||||
},
|
||||
"packages/plugin-browser-check": {
|
||||
"name": "@jspsych/plugin-browser-check",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-browser": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jspsych/config": "^1.0.0",
|
||||
"@jspsych/test-utils": "^1.0.0"
|
||||
@ -16961,7 +16970,8 @@
|
||||
"version": "file:packages/plugin-browser-check",
|
||||
"requires": {
|
||||
"@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": {
|
||||
@ -19332,6 +19342,11 @@
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||
|
@ -39,5 +39,8 @@
|
||||
"devDependencies": {
|
||||
"@jspsych/config": "^1.0.0",
|
||||
"@jspsych/test-utils": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"detect-browser": "^5.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,61 @@ jest.useFakeTimers();
|
||||
|
||||
describe("browser-check", () => {
|
||||
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([
|
||||
{
|
||||
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();
|
||||
|
||||
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";
|
||||
|
||||
const info = <const>{
|
||||
@ -10,16 +11,33 @@ const info = <const>{
|
||||
type: ParameterType.STRING,
|
||||
array: true,
|
||||
default: [
|
||||
"window_width",
|
||||
"window_height",
|
||||
"width",
|
||||
"height",
|
||||
"webaudio",
|
||||
"browser",
|
||||
"browser_version",
|
||||
"mobile",
|
||||
"os",
|
||||
"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
|
||||
*/
|
||||
@ -45,51 +63,115 @@ class BrowserCheckPlugin implements JsPsychPlugin<Info> {
|
||||
|
||||
constructor(private jsPsych: JsPsych) {}
|
||||
|
||||
private featureCheckFunctionsMap = new Map<string, () => any>(
|
||||
Object.entries({
|
||||
window_width: () => {
|
||||
return window.innerWidth;
|
||||
},
|
||||
window_height: () => {
|
||||
return window.innerHeight;
|
||||
},
|
||||
webaudio: () => {
|
||||
// @ts-ignore
|
||||
return (
|
||||
window.AudioContext ||
|
||||
window.webkitAudioContext ||
|
||||
window.mozAudioContext ||
|
||||
window.oAudioContext ||
|
||||
window.msAudioContext
|
||||
);
|
||||
},
|
||||
browser: () => {
|
||||
return "TODO";
|
||||
},
|
||||
browser_version: () => {
|
||||
return "TODO";
|
||||
},
|
||||
mobile: () => {
|
||||
return "TODO";
|
||||
},
|
||||
os: () => {
|
||||
return "TODO";
|
||||
},
|
||||
fullscreen: () => {
|
||||
return "TODO";
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
||||
const featureCheckFunctionsMap = new Map<string, () => any>(
|
||||
Object.entries({
|
||||
width: () => {
|
||||
return window.innerWidth;
|
||||
},
|
||||
height: () => {
|
||||
return window.innerHeight;
|
||||
},
|
||||
webaudio: () => {
|
||||
return (
|
||||
window.AudioContext ||
|
||||
// @ts-ignore
|
||||
window.webkitAudioContext ||
|
||||
// @ts-ignore
|
||||
window.mozAudioContext ||
|
||||
// @ts-ignore
|
||||
window.oAudioContext ||
|
||||
// @ts-ignore
|
||||
window.msAudioContext
|
||||
);
|
||||
},
|
||||
browser: () => {
|
||||
return detect().name;
|
||||
},
|
||||
browser_version: () => {
|
||||
return detect().version;
|
||||
},
|
||||
mobile: () => {
|
||||
return /Mobi/i.test(window.navigator.userAgent);
|
||||
},
|
||||
os: () => {
|
||||
return detect().os;
|
||||
},
|
||||
fullscreen: () => {
|
||||
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);
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const feature_data = new Map<string, any>();
|
||||
for (const feature of trial.features) {
|
||||
feature_data.set(feature, this.featureCheckFunctionsMap.get(feature)());
|
||||
const feature_checks: Promise<void>[] = [];
|
||||
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;
|
||||
// })
|
||||
);
|
||||
}
|
||||
|
||||
const trial_data = { ...Object.fromEntries(feature_data) };
|
||||
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();
|
||||
});
|
||||
|
||||
this.jsPsych.finishTrial(trial_data);
|
||||
var end_trial = () => {
|
||||
const trial_data = { ...Object.fromEntries(feature_data) };
|
||||
|
||||
this.jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
}
|
||||
|
||||
// MINIMUM SIZE
|
||||
|
Loading…
Reference in New Issue
Block a user