-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
345 lines (287 loc) · 11.6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
<!DOCTYPE html>
<html>
<head>
<title>Motor Study</title>
<!-- *** CSS *** -->
<link rel='stylesheet' type='text/css' href='jspsych_myStyle.css'>
<!-- *** JQUERY *** -->
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'></script>
<!-- *** JSPSYCH *** -->
<script src=jspsych.js></script>
<script src=jspsych-html-button-response.js></script>
<script src=jspsych-vmr.js></script>
<script src=jspsych-fullscreen.js></script>
<!-- *** FIREBASE *** -->
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="/__/firebase/7.13.2/firebase-app.js"></script>
<!-- Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="/__/firebase/7.13.2/firebase-analytics.js"></script> <!-- Analytics -->
<script src="/__/firebase/7.13.2/firebase-database.js"></script> <!-- Realtime Database -->
<!-- Configure Realtime Database config -->
<script>
// Set the configuration for your app
var config = {
//apiKey: "apiKey",
//authDomain: "projectId.firebaseapp.com",
databaseURL: "https://motorlearn-cb21c.firebaseio.com/",
//storageBucket: "bucket.appspot.com"
};
</script>
<!-- Initialize Firebase -->
<script src="/__/firebase/init.js"></script>
</head>
<!--THE EXPERIMENT -->
<body>
<script>
////////////////////////////////////////////////
// 0. Preliminaries
////////////////////////////////////////////////
// For: firebase.database().ref('experiments/' + expName + '/' workerId)
var expName = 'VMR-SR';
if (typeof(firebase) !== 'undefined'){
// Sign in user anonymously
firebase.auth().signInAnonymously().catch(function(error) {
// Handle errors
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/operation-not-allowed') {
alert('You must enable Anonymous auth in the Firebase Console.');
} else {
console.error(error);
}
});
// Listener for change in user state
firebase.auth().onAuthStateChanged(function(user){
if(user){
var uid = user.uid;
}else{
console.log('The user is not signed in.');
}
});
}
////////////////////////////////////////////////
// 1. Create structure for experiment
////////////////////////////////////////////////
/* Create empty TIMELINE */
var timeline = [];
/* Create SCREENMODE objects */
var screenmode = {
type: 'fullscreen',
fullscreen_mode: true
}
/* Create INSTRUCTIONS objects */
var instructions1 = {
type: 'html-button-response',
stimulus: '<h3>Welcome to the experiment</h3>' + "<br/>" +
"<p>Move the cursor from the home position to the target.<p>" + "<br/>" +
"<p>Let's start with some practice.<p>" + "<br/>",
choices: ['<h2>Start Practice</h2>'],
on_start: function() {$('body').css('cursor', 'auto')}, // show cursor before button click
on_finish: function() {$('body').css('cursor', 'none')} // hide cursor after button click
};
var instructions2 = {
type: 'html-button-response',
stimulus: '<h3>End of practice. Well done!</h3>' + "<br/>" +
"<p>Click the button to start the experiment.<p>" + "<br/>",
choices: ['<h2>Start Experiment</h2>'],
on_start: function() {$('body').css('cursor', 'auto')}, // show cursor before button click
on_finish: function() {$('body').css('cursor', 'none')} // hide cursor after button click
};
/* Create TRIAL objects */
// How many trials will you have of each type?
var trials = {
practice: 1,
baseline: 1, // 100 in McDougle Paper
initialPert: 1, // 200 "-"
counterPert: 1, // 20 "-"
errorClamp: 1 // 100 "-"
}
// note: in McDougle, 10% of baseline and initialPert trials are error_clamp trials
// compute total number of trials and initialize counter (for progress bar)
var totTrials = trials.practice+trials.baseline+trials.initialPert+trials.counterPert+trials.errorClamp;
var TrialCount = 1; // counts trials across all blocks (used for progress bar)
// initialize last mouse position variable (will be updated to track last position in previous trial)
// Will be used for re-calibration (WORK IN PROGRESS)
var lastMousePos = {
x: undefined,
y: undefined,
}
// A practice trial
var trial_practice = {
type: 'vmr',
perturbation_angle: 0,
data: { TrialType: 'Practice' }, // data that is saved for every type of that trial
on_finish: function(data) { getPreviousTrial(data); } // get some data from previous trial to update output/next trial
}
// A baseline trial
var trial_baseline = {
type: 'vmr',
perturbation_angle: 0,
data: { TrialType: 'Baseline' }, // data that is saved for every type of that trial
on_finish: function(data) { getPreviousTrial(data); } // get some data from previous trial to update output/next trial
}
// A perturbation trial
var trial_initialPert = {
type: 'vmr',
perturbation_angle: -15,
data: { TrialType: 'initialPert' }, // data that is saved for every type of that trial
on_finish: function(data) { getPreviousTrial(data); } // get some data from previous trial to update output/next trial
}
// A counter-perturbation trial
var trial_counterPert = {
type: 'vmr',
perturbation_angle: +15,
data: { TrialType: 'counterPert' }, // data that is saved for every type of that trial
on_finish: function(data) { getPreviousTrial(data); } // get some data from previous trial to update output/next trial
}
// An error-clamp trial
var trial_errorClamp = {
type: 'vmr',
error_clamp: true,
data: { TrialType: 'errorClamp' }, // data that is saved for every type of that trial
on_finish: function(data) { getPreviousTrial(data); } // get some data from previous trial to update output/next trial
}
/* Create BLOCK objects */
var practice_block = {
timeline: [trial_practice],
repetitions: trials.practice
}
var baseline_block = {
timeline: [trial_baseline],
repetitions: trials.baseline
}
var initialPert_block = {
timeline: [trial_initialPert],
repetitions: trials.initialPert
//randomize_order: false
}
var counterPert_block = {
timeline: [trial_counterPert],
repetitions: trials.counterPert
}
var errorClamp_block = {
timeline: [trial_errorClamp],
repetitions: trials.errorClamp
}
/* Create Firebase SAVE functions and block object */
function saveToFirebase(filedata){
var ref = firebase.database().ref('experiments/' + expName + '/' + firebase.auth.uid()).set(filedata, function(error) {
if (error) {
console.log('ERROR: Failed to save data to firebase.');
}
});
}
var save_screen = {
type: 'html-button-response',
stimulus: '<h3>The experiment is over. Thank you!</h3>' + "<br/>" +
"<p>Click the button to receive validation code.<p>" + "<br/>",
choices: ['<h2>Get Code</h2>'],
on_start: function() {$('body').css('cursor', 'auto')}, // show cursor
on_finish: function() {
if (typeof(firebase)!=="undefined"){
saveToFirebase(jsPsych.data.get().values());
}
}
}
if (typeof(firebase)!=="undefined"){
confirmationCode = firebase.auth.uid();
}else{
confirmationCode = '10027wl';
}
var code_screen = {
type: 'html-button-response',
stimulus: '<h3>This is your code:</h3>' + "<br/>",
choices: ['<h2>' + confirmationCode + '</h2>']
}
////////////////////////////////////////////////
// 2. Push everything to timeline
////////////////////////////////////////////////
timeline.push(screenmode);
timeline.push(instructions1);
timeline.push(practice_block);
timeline.push(instructions2);
timeline.push(baseline_block);
timeline.push(initialPert_block);
timeline.push(counterPert_block);
timeline.push(errorClamp_block);
timeline.push(save_screen);
timeline.push(code_screen);
////////////////////////////////////////////////
// 3. Start the experiment
////////////////////////////////////////////////
var browserInfo = getBrowserInfo();
if (browserInfo.browser !== 'Chrome' && browserInfo.browser !== 'Firefox') {
var wrong_browser = {
type: 'html-button-response',
stimulus: '<p>This experiment only has support for Google Chrome or Mozilla Firefox.</p>'
+ '<p>Please reopen the experiment in one of these browsers.</p>',
choices: ['Close window']}
jsPsych.init({
timeline: [wrong_browser],
on_finish: function(){window.close();}
});
}else{ // only run experiment if in correct browser
jsPsych.init({
timeline: timeline,
'show_progress_bar': true,
'auto_update_progress_bar': false,
'message_progress_bar': 'Completion Progress<br/>',
//on_start: function(){},
default_iti: 0
});
}
////////////////////////////////////////////////
// Extra functions
////////////////////////////////////////////////
/* Function checking browser */
function getBrowserInfo() {
var ua = navigator.userAgent, tem,
M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if(/trident/i.test(M[1])) {
tem= /\brv[ :]+(\d+)/g.exec(ua) || [];
return 'IE '+(tem[1] || '');
}
if(M[1]=== 'Chrome') {
tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
if(tem!= null)
return tem.slice(1).join(' ').replace('OPR', 'Opera');
}
M = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
if((tem= ua.match(/version\/(\d+)/i))!= null)
M.splice(1, 1, tem[1]);
return { 'browser': M[0], 'version': M[1] };
}
// Create five-character random string
function makeid(){
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
function createWorkerNode(workerId) {
firebase.database().ref('workers/' + workerId).set({
//username: name,
});
}
function createWorkerDataNode(workerId) {
firebase.database().ref('experiments/' + expName + '/' + workerId).set({
username: name,
email: email,
profile_picture : imageUrl
});
}
/* Function getting data from last trial to update global variables */
function getPreviousTrial(data) { // pass data recorded in previous trial to access last mouse position & add global output variables
data.TrialNumber = TrialCount; // add trial number to output
jsPsych.setProgressBar(TrialCount/totTrials);
TrialCount += 1;
// get last mouse position from previous trial to update mouseOffset for current trial
lastMousePos.x = data.cursorX(-1)[0];
lastMousePos.y = data.cursorY(-1)[0];
//mouseOffset.x =
}
</script>
</body>
</html>