-
Notifications
You must be signed in to change notification settings - Fork 2
/
cont_omst.html
558 lines (510 loc) · 23.8 KB
/
cont_omst.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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
<!DOCTYPE html>
<html>
<!--
Task: cMST (clinical-optimized MST) Continuous version
Author: Craig Stark
Forked from main cMST_cs_v1 on 5/7/20 and modified for the more traditional continuous MST
Forked again on 8/20/20, pulled out Cordova, and setup for JATOS
Currently, this is set to do a 256-trial continuous design with 32 lures & repeat pairs at
"short" lags of 4-12 and 32 lure and repeat pairs at "medium" lags of 20-100
Revised: November 20, 2019 (CELS) for Cordova
12/2/19 (CELS): using image height/width (new addition to plugin) and if end, returns to param screen
12/4/19 (CELS): Using .replace not .href and will take you back to index.html if the end
12/5/19 (CELS): Styles to alter text/button sizes, ensuring deviceready, meta-tag
12/6/19 (CELS): Fixed bug where stim-set wasn't being honored
12/6/19 (CELS): Shifted data saving to savedata.js and setup to allow local backup save on iOS
12/18/19 (CELS): Added d' outputs
4/13/20 (CELS): Added cmstset parameter
4/13/20 (CELS): Goes to end.html if this is the last phase
4/16/20 (CELS): Will detect if on SONA and go back there for credit
5/7/2020 (CELS): Fixed issue in d' calculations
5/13/2020 (CELS): Better fix for d' calculations
6/2/2020 (CELS): Fix bug in false-alarm to foil rate
8/20/20 (CELS): Forked off and reworked for JATOS
9/24/20 (CELS): Updated the commenting
9/25/20 (CELS): Added ability to have componentJson "force" some of the parameters
10/1/20 (CELS): No longer show data summary at end. Log this to the main data field for easy extraction
Will post results summary to JATOS batch data
12/1/20 (CELS): Added twochoice flag (default=1) and, if 0, will do traditional old/similar/new
1/3/22 (CELS): Added ability to force the order file prefix (force_orderprefix)
3/14/22 (CELS): Reworked a bit for being our continuous version of the cMST_v2.
Set defaults to 3-choice and will used the imbalanced orders.
Removed the video instructions
Renamed to cont_cmst
Phasename set to cMSTCont
Set to use version-based jspsych folder
3/18/22 (CELS): Fixed old jspsych button/keyboard response code
5/3/22 (CELS): Added self-paced mode
5/31/22 (CELS): Added initial "instr1_trial" to fix iPad issue
7/12/22 (CELS): Fixed space-bar start bug
Added logging of lbin if it exists in the order file
10/21/22 (CELS): Updated default to buttons and updated default order file based on Stark et al. "Optmizing ..."
11/2/22 (CELS): Setup to allow "baseurl" to specify a web-based location for the images
2/28/23 (CELS): Shifted to jsPsych 7 and added preloading images
3/7/23: (CELS): Changed the stim-set variable to set_omst
8/16/23 (CELS): Fixed for versions without setup.html to progress to next phase
8/30/23 (CELS): Added getID() to come up with a good sid
9/10/23 (CELS): Shifted the orderprefix to the new cMST_Imbal2x3_orders and to jsons. These ditch
the notion of "run" and replace them with a real sub-set as we get 3 subsets per set.
Allows both "set" and "set_omst" to define the main set.
9/27/23 (CELS): Logging browser info
10/12/23 (CELS): Previous two pulled in from IP_OL exp
10/23/23 (CELS): Multi-lang support using Honeycomb style language file
Optional parameters:
In the JATOS versions, these come in via jatos.studySessionData while in Cordova, we pass
them in as URL parameters. Check the code as the actual variable names differ a touch b/n versions.
(these are the JATOS ones)
Optional parameters:
[sid=##]: Subject ID -- used for data file name (default=1234)
[resp_mode=X]: Response mode -- set to 'keyboard' to use keyboard, anything else to use buttons (default=buttons)
[set=#]: Stimulus set -- 1-6 (1=default) -- used in loading the order file
[subset=XX]: Which particular subset? (1-3, 1=default) -- Like the old 1a/1b, this gives 3 subsets per set for 18 total tests
[set_omst=#]: Same as above, but even over-rides the above. Useful if using the cMST and MST in the same experiment
[trialorder=XX]: Which base order file? (1-4, 1=default) -- controls ordering of conditions in a run
[twochoice=X]: 0=OSN, 1=ON response choices (0=default)
[selfpaced=X]: Should we allow infinite time with blank screen to make the response? (default =1)
Note, if there is a studySessionData variable called "order" (and one called "taskindex") it will use this to
queue up the next task.
More parameters:
You can set the component's JSON input to:
[force_set=X]: Force a stimulus set (1-6)
[force_twochoice=X]
[force_orderprefix=X]: Force a particular stimulus order.
Note - needs order files in 'jsOrder' and needs image files in 'Set #_rs'
order files are jsOrders/cMST_Imbal2x3_orders_[set]_[order]_[subset].js
Note, if you use the "baseurl" bit to specify a webserver with the images, you'll need to update the Content-Security-Policy below
For example, I use: img-src 'self' https://starklab.bio.uci.edu data: content:;
-->
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: 'unsafe-inline' 'unsafe-eval'
https://fonts.gstatic.com ;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/css; media-src *;
img-src 'self' https://starklab.bio.uci.edu data: content:;">
<script type="text/javascript" src="jatos.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script src="js/jquery-3.1.1.min.js"></script>
<script src="js/jspsych_731/dist/jspsych.js"></script>
<script src="js/jquery-3.1.1.min.js"></script>
<script src="js/jspsych_731/dist/plugin-html-keyboard-response.js"></script>
<script src="js/jspsych_731/dist/plugin-image-keyboard-response.js"></script>
<script src="js/jspsych_731/dist/plugin-html-button-response.js"></script>
<script src="js/jspsych_731/dist/plugin-image-button-response.js"></script>
<script src="js/jspsych_731/dist/plugin-video-button-response.js"></script>
<script src="js/jspsych_731/dist/plugin-video-keyboard-response.js"></script>
<script src="js/jspsych_731/dist/plugin-preload.js"></script>
<script src="js/jspsych_731/dist/plugin-browser-check.js"></script>
<link rel="stylesheet" href="css/jspsych.css"></link>
<style>
.jspsych-display-element {
font-size: 200%;
}
.jspsych-btn {
font-size: 150%;
}
</style>
</head>
<body></body>
<script>
function waitFor(conditionFunction) {
const poll = resolve => {
if(conditionFunction()) resolve();
else setTimeout(_ => poll(resolve), 400);
}
return new Promise(poll)
}
function invNormcdf(p) { // https://stackoverflow.com/questions/8816729/javascript-equivalent-for-inverse-normal-function-eg-excels-normsinv-or-nor
var a1 = -39.6968302866538, a2 = 220.946098424521, a3 = -275.928510446969;
var a4 = 138.357751867269, a5 = -30.6647980661472, a6 = 2.50662827745924;
var b1 = -54.4760987982241, b2 = 161.585836858041, b3 = -155.698979859887;
var b4 = 66.8013118877197, b5 = -13.2806815528857, c1 = -7.78489400243029E-03;
var c2 = -0.322396458041136, c3 = -2.40075827716184, c4 = -2.54973253934373;
var c5 = 4.37466414146497, c6 = 2.93816398269878, d1 = 7.78469570904146E-03;
var d2 = 0.32246712907004, d3 = 2.445134137143, d4 = 3.75440866190742;
var p_low = 0.02425, p_high = 1 - p_low;
var q, r;
var retVal;
if ((p < 0) || (p > 1))
{
alert("NormSInv: Argument out of range.");
retVal = 0;
}
else if (p < p_low)
{
q = Math.sqrt(-2 * Math.log(p));
retVal = (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
}
else if (p <= p_high)
{
q = p - 0.5;
r = q * q;
retVal = (((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q / (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1);
}
else
{
q = Math.sqrt(-2 * Math.log(1 - p));
retVal = -(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
}
return retVal;
}
function leftFillNum(num, width){
return num
.toString()
.padStart(width,0)
}
function getID() {
// Try to get a reasonable ID code for this person. You can use the URL with a "sid" parameter or
// specify one in the jatos studySessionData. If not, it'll use the JATOS workerId.
// URL > studySession > workerID
var sid=jatos.urlQueryParameters.sid;
if (sid == undefined) {
sid=jatos.studySessionData['sid'];
}
if (typeof sid == 'undefined') {
if (typeof jatos.workerId !== 'undefined') { // At least try the workerID
sid = jatos.workerId;
}
else { sid=1234; }
}
return sid
}
jatos.onLoad(async function() {
baseurl='https://starklab.bio.uci.edu/mst/'; // How we'll get images, videos, etc. Set to empty string to use local folders
//baseurl=''; // How we'll get images, videos, etc. Set to empty string to use local folders
var sid=getID();
var resp_mode='button';
if (jatos.studySessionData['resp_mode'] == 'keyboard') {
resp_mode='key';
}
else if (jatos.studySessionData['resp_mode'] == 'key') {
resp_mode='key';
}
var stim_set='1';
if (typeof jatos.studySessionData['set'] !== 'undefined') { // Try here first
stim_set = jatos.studySessionData['set']
}
if (typeof jatos.studySessionData['set_omst'] !== 'undefined') { //Try here 2nd / override
stim_set = jatos.studySessionData['set_omst']
}
if (jatos.componentJsonInput && typeof jatos.componentJsonInput['force_set'] !== 'undefined') {
stim_set=jatos.componentJsonInput['force_set'];
console.log('Stim set forced via JSON component to ' + stim_set);
}
var trialorder='1';
if (typeof jatos.studySessionData['trialorder'] !== 'undefined') {
trialorder=jatos.studySessionData['trialorder'];
}
var subset='1';
if (typeof jatos.studySessionData['subset'] !== 'undefined') {
subset=jatos.studySessionData['subset'];
}
if (jatos.componentJsonInput && typeof jatos.componentJsonInput['force_subset'] !== 'undefined') {
subset=jatos.componentJsonInput['force_subset'];
console.log('subset forced via JSON component to ' + subset);
}
var twochoice=0;
if (typeof jatos.studySessionData['twochoice'] !== 'undefined') {
twochoice = jatos.studySessionData['twochoice']
}
if (jatos.componentJsonInput && typeof jatos.componentJsonInput['force_twochoice'] !== 'undefined') {
twochoice=jatos.componentJsonInput['force_twochoice'];
console.log('Two-choice mode forced via JSON component to ' + twochoice);
}
var selfpaced=1;
if (typeof jatos.studySessionData['selfpaced'] !== 'undefined') {
selfpaced = jatos.studySessionData['selfpaced']
}
var orderprefix = 'jsOrders/cMST_Imbal2x3_orders_';
if (jatos.componentJsonInput && typeof jatos.componentJsonInput['force_orderprefix'] !== 'undefined') {
orderprefix=jatos.componentJsonInput['force_orderprefix'];
console.log('Order prefix forced via JSON component to ' + orderprefix);
}
var orderfile = orderprefix+stim_set+'_'+trialorder+'_'+subset+'.json';
console.log('loading json order: ' + orderfile)
var trial_stim=null;
$.getJSON(orderfile,function( data ) {
trial_stim=data;
console.debug(orderfile + ' loaded...ish');
});
await waitFor(_ => trial_stim !== null);
console.log('we are set')
console.log(trial_stim.length)
const preload_fnames = new Array(trial_stim.length)
for (var i=0; i<trial_stim.length; i++) { // Make our Set X_rs be SetX_rs
trial_stim[i].image=baseurl+trial_stim[i].image.replace("Set ","Set");
preload_fnames[i]=trial_stim[i].image;
}
var lang='en';
if (typeof jatos.studySessionData['lang'] !== 'undefined') {
lang=jatos.studySessionData['lang'];
}
if (jatos.studyJsonInput && typeof jatos.studyJsonInput['lang'] !== 'undefined' ) {
lang=jatos.studyJsonInput['lang']
}
if (jatos.batchJsonInput && typeof jatos.batchJsonInput['lang'] !== 'undefined' ) {
lang=jatos.batchJsonInput['lang']
}
// load honeycomb version of lang file
console.log('loading json lang')
var langfile='lang/omst_'+lang+'.json';
var json_prompts=null;
$.getJSON(langfile,function( data ) {
json_prompts=data;
console.debug(langfile + ' loaded...ish');
});
await waitFor(_ => json_prompts !== null);
console.log(json_prompts['task']['name'])
let prompts=json_prompts['cont'] // Load in this phase's section
// START OF CODE THAT SHOULD BE CONSTANT REGARDLESS OF JATOS / CORDOVA
const phasename='oMSTCont';
var jsPsych = initJsPsych({on_finish: function() {
if (0) { jsPsych.data.displayData(); }
else {
var order=jatos.studySessionData["order"];
jatos.studySessionData["taskindex"] += 1;
var expdata = jsPsych.data.get().json();
if (typeof order === 'undefined') {
// We don't have an 'order' setup, so assume it's 1-N
console.log('faking an order')
order=Array(jatos.componentList.length).fill().map((e,i)=>i+1);
jatos.studySessionData["taskindex"]=jatos.componentPos;
//console.log(order);
//console.log(jatos.studySessionData["taskindex"]);
}
// Submit results to JATOS and queue the end or next task
if (typeof order === 'undefined' || order.length == jatos.studySessionData["taskindex"]) {
// we're done
// Check if this came from SONA - should have URL.sid and .sona
if (typeof jatos.urlQueryParameters.sid === 'undefined' || typeof jatos.urlQueryParameters.sona === 'undefined' ||
typeof jatos.studyJsonInput['experiment_id'] === 'undefined' || typeof jatos.studyJsonInput['credit_token'] === 'undefined') {
jatos.submitResultData(expdata,jatos.endStudy);
}
else {
// This is here for SONA experiments. You'll want to tweak it for your own online setup, but this gives you the return to them for credit
var redirect='https://uci.sona-systems.com/webstudy_credit.aspx?experiment_id='+jatos.studyJsonInput['experiment_id']+
'&credit_token='+jatos.studyJsonInput['credit_token']+'&survey_code='+jatos.urlQueryParameters.sid;
jatos.endStudyAndRedirect(redirect,expdata);
}
}
else {
// submit and start the next
jatos.submitResultData(expdata, () => { jatos.startComponentByPos(order[jatos.studySessionData["taskindex"]]) });
}
}
}
});
jsPsych.data.addProperties({
task: phasename,
subject: sid,
set: stim_set,
selfpaced: selfpaced,
orderfile: orderfile
});
// Setup prompts and response options based on keyboard/button and 2/3 choices
// var instr_choice=[' ']; // 32 is space
// var instr_txt='<i>spacebar</i>';
// var trial_txt='Old (<b>v</b>), Similar (<b>b</b>), or New? (<b>n</b>)';
// var trial_choices=['v','b','n'];
if (twochoice == 1) {
var trial_choices=[ prompts[resp_mode]['twochoice']['old'], prompts[resp_mode]['twochoice']['new'] ];
}
else {
console.log(prompts[resp_mode])
console.log(prompts[resp_mode]['threechoice']['trial_choices'])
var trial_choices= [prompts[resp_mode]['threechoice']['trial_choices']['old'],
prompts[resp_mode]['threechoice']['trial_choices']['sim'],
prompts[resp_mode]['threechoice']['trial_choices']['new'] ];
}
// trial_txt='Old (<b>v</b>) or New? (<b>n</b>)';
// trial_choices=['v','n'];
// }
// if (resp_mode == 'button') {
// instr_choice=['OK'];
// instr_txt='<i>OK</i>';
// if (twochoice == 1) {
// trial_txt='<i>Old</i> or <i>New</i>';
// trial_choices=['Old','New'];
// }
// else {
// trial_txt='<i>Old</i>, <i>Similar</i>, or <i>New</i>';
// trial_choices=['Old','Similar','New'];
// }
// }
let choicekey='threechoice';
if (twochoice == 1) { choicekey="twochoice" }
var instr1_trial = {
type: (resp_mode == 'button' ? jsPsychHtmlButtonResponse : jsPsychHtmlKeyboardResponse),
choices: [prompts[resp_mode]['instr_choice']],
prompt: "<p>" +prompts[resp_mode]['instr_prompt'] + "</p>",
margin_horizontal: '40px', margin_vertical: '20px',
// button_html: '<button style="font-size: 150%" class="jspsych-btn">%choice%</button>',
stimulus: "<p>" +prompts[resp_mode][choicekey]['instr_stim'] + "</p>",
}
// Build up my actual trial timeline
var tlv = [];
var ntrials = trial_stim.length;
let DEBUGMODE=0;
if (DEBUGMODE == 1) { ntrials = 20; }
console.log('Building up the ' + ntrials + ' trials')
for (var i=0; i<ntrials; i++) {
// in corr_resp: 0=old, 1=sim, 2=new
trial_info=trial_stim[i]
let tr_type='foil';
let cresp='n'
if (trial_info.correct_resp == 0) {
tr_type = 'target';
cresp='o';
}
else if (trial_info.correct_resp == 1) {
tr_type='lure';
if (twochoice == 1) {
cresp='n';
}
else {
cresp='s';
}
}
let lure_bin=0; // We may or may not have this in the order file
if (trial_info.lbin && trial_info.lbin !== 'undefined') {
lure_bin=trial_info.lbin;
}
// keycode 'n' (for 1 and 2) = 78, 'y' (for 0)=89
// keycode 'n' (for 1 and 2) = 78, 'y' (for 0)=89, i=73, o=79
//let obj={stimulus: trial_info.image, data: {condition: tr_type, correct_response: cresp, lbin:lure_bin}}
let obj={stimulus: trial_info.image, data: {condition: tr_type, correct_response: cresp, lbin:lure_bin}}
//console.log(i + ' bin: ' + lure_bin)
tlv.push(obj)
}
//console.log(JSON.stringify(tlv))
var test_trials = {
timeline: [
{
type: (resp_mode == 'button' ? jsPsychImageButtonResponse : jsPsychImageKeyboardResponse),
prompt: "<p>" +prompts[resp_mode][choicekey]['trial_prompt'] + "</p>",
choices: trial_choices,
stimulus_duration: 2000,
trial_duration: (selfpaced==1 ? null : 2500),
post_trial_gap: 500,
margin_horizontal: '40px', margin_vertical: '20px',
stimulus: jsPsych.timelineVariable('stimulus'),
data: jsPsych.timelineVariable('data'),
stimulus_height: 400,
stimulus_width: 400,
on_finish: function(data) {
// yes = button 0 = 'y' = keycode 89
// no = button 1 = 'n' = keycode 78
// let resp = 'n';
// if (resp_mode == 'button' && data.button_pressed == 0) { resp = 'y' }
// if (resp_mode == 'keyboard' && data.key_press == 89 ) { resp = 'y' }
let resp = null;
if (resp_mode == 'button') {
if (data.response == 0) { resp = 'o' }
else if (data.response == 2 ) { resp = 'n' }
else if (data.response == 1 ) { resp = (twochoice==1 ? 'n' : 's') }
}
else {
if (data.response == 'v') { resp = 'o' }
else if (data.response == 'b' ) { resp = 's' }
else if (data.response == 'n') { resp = 'n' }
}
data.correct = resp == data.correct_response;
data.resp = resp;
}
}],
timeline_variables: tlv
}
var debrief_block = {
type: jsPsychHtmlKeyboardResponse,
trial_duration: 500,
stimulus: prompts['ty'],
on_finish: function (data) {
let validtrials=jsPsych.data.get().filterCustom(function(trial) {
return trial.resp !== null;
})
let targets=validtrials.filter({condition: 'target'});
let lures=validtrials.filter({condition: 'lure'});
let foils=validtrials.filter({condition: 'foil'});
if (twochoice==1) {
let corr_targs = targets.filter({correct: true});
let corr_lures = lures.filter({correct: true});
let corr_foils = foils.filter({correct: true});
let hits=Math.round(corr_targs.count() / targets.count() * 100);
let cr_lure=Math.round(corr_lures.count() / lures.count() * 100);
let cr_foil=Math.round(corr_foils.count() / foils.count() * 100);
let p_fa_foil = 0.0;
let p_fa_lure = 0.0;
let p_hit = 0.0;
if (corr_targs.count() == 0) { p_hit = 0.5 / targets.count(); }
else if (corr_targs.count() == targets.count() ) { p_hit = (targets.count() - 0.5) / targets.count(); }
else { p_hit=corr_targs.count() / targets.count(); }
if (corr_lures.count() == lures.count()) { p_fa_lure = 0.5 / lures.count(); }
else if (corr_lures.count() == 0 ) { p_fa_lure = (lures.count() - 0.5) / lures.count(); }
else { p_fa_lure=1-(corr_lures.count() / lures.count());}
if (corr_foils.count() == foils.count()) { p_fa_foil = 0.5 / foils.count(); }
else if (corr_foils.count() ==0 ) {p_fa_foil = (foils.count() - 0.5) / foils.count(); }
else { p_fa_foil=1-(corr_foils.count() / foils.count()); }
console.log(corr_targs.count() + " " + targets.count() + " " + p_hit)
console.log(corr_lures.count() + " " + lures.count() + " " + p_fa_lure)
console.log(corr_foils.count() + " " + foils.count() + " " + p_fa_foil)
console.log(invNormcdf(p_hit))
console.log(invNormcdf(p_fa_lure))
console.log(invNormcdf(p_fa_foil))
let dpTF = invNormcdf(p_hit) - invNormcdf(p_fa_foil);
let dpTL = invNormcdf(p_hit) - invNormcdf(p_fa_lure);
var retstr = 'HR, ' + hits + ', CR-L, ' + cr_lure + ', CR-F rate, ' + cr_foil + ", d'T:F, " + dpTF.toFixed(3) + ", d'T:L, " + dpTL.toFixed(3)
}
else { // OSN
let targ_old=targets.filter({resp: 'o'});
let targ_sim=targets.filter({resp: 's'});
let targ_new=targets.filter({resp: 'n'});
let lure_old=lures.filter({resp: 'o'});
let lure_sim=lures.filter({resp: 's'});
let lure_new=lures.filter({resp: 'n'});
let foil_old=foils.filter({resp: 'o'});
let foil_sim=foils.filter({resp: 's'});
let foil_new=foils.filter({resp: 'n'});
let rec=(targ_old.count() / targets.count()) - (foil_old.count() / foils.count());
let ldi=(lure_sim.count() / lures.count()) - (foil_sim.count() / foils.count());
var retstr='Valid, ' + targets.count() + ", " + lures.count() + ', ' + foils.count() + '\n';
retstr += 'Old, ' + targ_old.count() + ", " + lure_old.count() + ', ' + foil_old.count() + '\n';
retstr += 'Similar, ' + targ_sim.count() + ", " + lure_sim.count() + ', ' + foil_sim.count() + '\n';
retstr += 'New, ' + targ_new.count() + ", " + lure_new.count() + ', ' + foil_new.count() + '\n';
retstr += 'REC, ' + rec.toFixed(3) + ', LDI, ' + ldi.toFixed(3);
}
let date = new Date();
let dcode = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + (date.getDate()+1) +
"-" + date.getHours() + "-" + date.getMinutes() + "-" + date.getSeconds();
if (!jatos.batchSession.defined("/" + sid)) { // Should have this by now, but to be safe -- make sure to create this as an array
jatos.batchSession.add("/" + sid,[phasename+"_"+dcode+"_"+retstr]);
}
else { // Append to array
jatos.batchSession.add("/" + sid + "/-",phasename+"_"+dcode+"_"+retstr);
}
data.summary = retstr;
//jatos.batchSession.add("/"+idcode,{ [phasename + "_results"]: data.summary });
//return '<p>Hit rate: ' + hits + '</p><p>CR-L rate: ' + cr_lure + '</p><p>CR-F rate: ' + cr_foil + '</p>'
//return 'HR, ' + hits + ', CR-L, ' + cr_lure + ', CR-F rate, ' + cr_foil + ", d'T:F, " + dpTF.toFixed(3) + ", d'T:L, " + dpTL.toFixed(3)
}
}
// END OF CONSTANT CODE
var preload = {
type: jsPsychPreload,
images: preload_fnames, // since we use a timeline variable, we can't use the simple "trials"
show_progress_bar: true,
show_detailed_erros: true,
continue_after_error: true,
on_error: function(fname) {
console.log('FAILED '+fname)
},
on_finish: function(data) {
console.log('Preload success? ' + data.success)
console.log('Failed on ' + data.failed_images.length)
}
}
var browserinfo = {
type: jsPsychBrowserCheck
};
var timeline = [browserinfo, preload, instr1_trial, test_trials, debrief_block];
jsPsych.run(timeline);
});
</script>
</html>