Repository navigation
Expand file tree
/
Copy pathps_dev_task.js
More file actions
1464 lines (1198 loc) · 50 KB
/
Copy pathps_dev_task.js
File metadata and controls
1464 lines (1198 loc) · 50 KB
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//IN THIS VERSION, THE KEYBOARD RESPONSE IS CODED WITH KEYDROWN.JS
//IF RESPONSE IS NOT DEPENDENT ON KEY DELAY REPEAT RATE
/**********************
* Domain general code *
**********************/
//-this section declares functions for later use in the script
// Helper functions
// Assert functions stolen from
// http://aymanh.com/9-javascript-tips-you-may-not-know#assertion
function AssertException(message) { this.message = message; }
AssertException.prototype.toString = function () {
return 'AssertException: ' + this.message;
};
function assert(exp, message) {
if (!exp) {
throw new AssertException(message);
}
}
function numberRange (start, end) {
return new Array(end - start).fill().map((d, i) => i + start);
}
//function to compile data into form to save
function insert_hidden_into_form(findex, name, value ) {
var form = document.forms[findex];
var hiddenField = document.createElement('input');
hiddenField.setAttribute('type', 'hidden');
hiddenField.setAttribute('name', name);
hiddenField.setAttribute('value', value );
form.appendChild( hiddenField );
}
//function to shuffle numbers
function shuffle(o){
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
//function to calculate normal distribution
// Standard Normal variate using Box-Muller transform.
function randn() {
var u = 0, v = 0;
while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
while(v === 0) v = Math.random();
return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
}
//summing numbers in arrays together
Array.prototype.SumArray = function (arr) {
var sum = this.map(function (num, idx) {
return num + arr[idx];
});
return sum;
};
//modulus after division functions
function mod(x,y){
var mod = x - Math.floor(x/y)*y;
return mod;
}
// linspace function
function linspace(a,b,n) {
if(typeof n === "undefined") n = Math.max(Math.round(b-a)+1,1);
if(n<2) { return n===1?[a]:[]; }
var i,ret = Array(n);
n--;
for(i=n;i>=0;i--) { ret[i] = (i*b+(n-i)*a)/n; }
return ret;
}
//function to calculate coordinates on the circle. the position is basically the angle.
function circleCoor(steps, centerX, centerY, radius) {
var xValues1=[];
var yValues1=[];
for (i = 0; i < steps; i++) {
var xValues = (centerX + radius * Math.cos(Math.PI * i / steps*2-Math.PI/2));
var yValues = (centerY + radius * Math.sin(Math.PI * i / steps*2-Math.PI/2));
var xValues1=xValues1.concat(xValues);
var yValues1=yValues1.concat(yValues);
}
return { x:xValues1,
y:yValues1
};
}
function add(a, b) {
return a + b;
}
function range (low, high, step) {
var matrix = [];
var inival, endval, plus;
var walker = step || 1;
var chars = false;
if (!isNaN(low) && !isNaN(high)) {
inival = low;
endval = high;
} else if (isNaN(low) && isNaN(high)) {
chars = true;
inival = low.charCodeAt(0);
endval = high.charCodeAt(0);
} else {
inival = (isNaN(low) ? 0 : low);
endval = (isNaN(high) ? 0 : high);
}
plus = ((inival > endval) ? false : true);
if (plus) {
while (inival <= endval) {
matrix.push(((chars) ? String.fromCharCode(inival) : inival));
inival += walker;
}
} else {
while (inival >= endval) {
matrix.push(((chars) ? String.fromCharCode(inival) : inival));
inival -= walker;
}
}
return matrix;
}
//function to convert confidence rating into its position on the bar
function confConvert(canvas_xCenter,conf_barWidth,newConf)
{
var conf_pos = (canvas_xCenter-conf_barWidth/2)+5*newConf;
return conf_pos;
}
//function for random integer
function randomIntFromInterval(min,max)
{
return Math.floor(Math.random()*(max-min+1)+min);
}
/********************
* TASK-GENERAL CODE *
********************/
//-this section declares the structure of the entire experiment (that includes questionnaire items after the task)
// Globals defined initially.
var keydownfun = function() {};
// Mutable global variables
var datastring =""; // empty string to save task data
var questDatastring =""; //empty string to save questionnaire data
var payment="";
var bonusPayFin=0; // participants are given a bonus up to 1USD the higher amount of points they score
var set_cond = shuffle([1,2]); // set conditions for the task
var is_training = true; // hasn't started practice trials yet
var is_quiz = false; //is going to do the instruction quiz
var q = -1; //this the counter for the questionnaires
var last_block= false;
var data_expReset = 1; //this is the counter for how many times they reset the exp bc they failed the confidence
var data_pracReset = 1; //this is the counter for how many times they reset the prac bc they failed the instructions
/*************
* Finishing up related stuff *
*************/
//BEGINING WITH INSTRUCTIONS FOR THE TASK AND THE TUTORIAL
//this is the first page,
var demoquestionnaire = function(){
// var timestamp = new Date().getTime();
showpage('ps_instruct_demoq');
$("#continue").click(function () {
finish();
startInstruc();
});
};
//exceute three instruction pages
var startInstruc = function(){
instructobject = new Instructions(['ps_instruct_1','ps_instruct_2','ps_instruct_3']);
finish();
startTask();
};
//this function starts the quitter code - i.e. will save incomplete data and warn about leaving the experiment
var startTask = function () {
window.onbeforeunload = function(){
console.log("Premature quitting!");
var endTime = new Date().toISOString().substr(0, 19);
$.ajax("quitter", {
type: "POST",
async: false,
data: {workerId: workerId,assignmentId:assignmentId, hitId:hitId, datastring: datastring,when:endTime}
});
// $("<div title='Warning!'>By leaving this page, you opt out of the experiment. You are forfeiting your payment. Please ensure that this is what you meant to do.</div>").dialog();
alert( "By leaving this page, you opt out of the experiment. You are forfeiting your payment. Please confirm that this is what you meant to do." );
return "By leaving this page, you opt out of the experiment. You are forfeiting your payment. Please confirm that this is what you meant to do. Are you sure you want to leave the experiment?";
};
};
//this function starts the instruction quiz
var instruction_quiz = function(){
showpage('ps_instruct_quiz');
$("#continue").click(function () {
finish();
CheckAnswers();
});
};
//checking the answers of the instruction quiz to determine if they repeat the tutorial or move on to the task
var CheckAnswers = function() {
$('select').each( function(i, val) {
//correct answers are 4, 3, 3, 2, 4
if(this.id =="coinAimCheck"){
correct = (this.value == 4)? 1 : 0;
if(correct === 0){
instructobject = new Instructions(['ps_instruct_0','ps_instruct_1','ps_instruct_2', 'ps_instruct_3']);
data_pracReset++;
is_quiz = true;
is_training = true;
startTask();
return false;
}
}
else if(this.id =="coinFlyCheck"){
correct = (this.value == 3)? 1 : 0;
if(correct === 0){
instructobject = new Instructions(['ps_instruct_0','ps_instruct_1','ps_instruct_2','ps_instruct_3']);
data_pracReset++;
is_quiz = true;
is_training = true;
startTask();
return false;
}
}
else if(this.id == "confRateCheck"){
correct = (this.value == 3)? 1 : 0;
if(correct === 0){
instructobject = new Instructions(['ps_instruct_0','ps_instruct_1','ps_instruct_2', 'ps_instruct_3']);
data_pracReset++;
is_quiz = true;
is_training = true;
startTask();
return false;
}
}
else if(this.id =="HighConfCheck"){
correct = (this.value == 2)? 1 : 0;
if(correct === 0){
instructobject = new Instructions(['ps_instruct_0','ps_instruct_1','ps_instruct_2','ps_instruct_3']);
data_pracReset++;
is_quiz = true;
is_training = true;
startTask();
return false;
}
}
else if(this.id == "LowConfCheck"){
correct = (this.value == 4)? 1 : 0;
if(correct === 0){
instructobject = new Instructions(['ps_instruct_0','ps_instruct_1','ps_instruct_2', 'ps_instruct_3']);
data_pracReset++;
is_quiz = true;
is_training = true;
startTask();
return false;
}
}
});
if(correct ==1){ //if all correct, head on to start page for actual experiment
instructobject = new Instructions(['ps_instruct_4']);
is_quiz = false; //finished instruction quiz
is_training = false;//just going to start practice round
startTask();
}
};
var finish = function () {
window.onbeforeunload = function(){ };
};
//this is needed because if keys are on, then cannot type for the medlist questSection (one section in the psychiatric questionnaire)
//hence this is to remove the eventlisteners
var removeListener = function (){
$(window).off('keydown');
$(window).off('keyup');
$(window).off('blur');
};
//this is to execute the psychiatric questionnaire portion
var questSection = function(){
kd.stop(); //to stop the kd run that was ran earlier (no keyboard response from now on!)
removeListener(); //this is so that you can type in the medlist
var endTime = new Date().toISOString().substr(0, 19);
showpage('end');
$("#continue").click(function () {
finish();
});
// payment details needed - mTurk IDs and the amount of bonus pay they earned
payment = payment.concat(assignmentId);
payment = payment.concat("\t", workerId);
payment = payment.concat("\t", bonusPayFin);
// save data function
$.ajax("done", {
type: "POST",
async: false,
data: {workerId: workerId,assignmentId:assignmentId, hitId:hitId, datastring: datastring, payment:payment, when:endTime},
});
return false;
};
/********************
* HTML snippets
********************/
var pages = {};
var showpage = function(pagename) {
$('body').html( pages[pagename] );
};
var pagenames = [
"ps_instruct_quiz",
"exp",
"end"
];
/************************
* CODE FOR INSTRUCTIONS *
************************/
//--this is to execute the instruction quiz
var Instructions = function( screens ) {
var that = this,
currentscreen = "",
endTime;
for( i=0; i<screens.length; i++) {
pagename = screens[i];
if(pagename.indexOf('instruct') !== -1){ //(pagename.slice(0,8)==='instruct'){
$.ajax({
url: pagename + ".html",
success: function(pagename){ return function(page){ pages[pagename] = page; }; }(pagename),
async: false
});
}
}
//if the next page is the quiz, run the quiz script
this.nextForm = function () {
var next = screens.shift();
if(next === 'is_quiz'){
instruction_quiz();
return false;
}
currentscreen = next;
showpage( next );
// timestamp = new Date().getTime();
if ( screens.length === 0 ){
$('.continue').click(function() {
// that.recordtrial();
that.startTask();
});
}
else{
$('.continue').click( function() {
// that.recordtrial();
that.nextForm();
});
}
};
//when its time to start the task experiment, start new experimental phase
this.startTask = function() {
$.ajax({
url: "exp.html",
success: function(pagename){ return function(page){ pages[pagename] = page; }; }(pagename),
async: false
});
testobject = new ExptPhase();
};
this.nextForm();
};
/********************
* FOR KEYDROWN (needs to be run ONLY ONCE, HENCE OUT OF THE EXPTPHASE) *
********************/
kd.run(function () {
kd.tick();
});
/********************
* CODE FOR TEST *
********************/
// this is the code pertaining to the experimental TASK
var ExptPhase = function() {
var i;
// var window_focus = true;
var that = this; // make 'this' accessble by privileged methods
// for keyboard functions
//var leftKey = 37;
//var rightKey = 39;
//var spaceKey = 32;
//colour details
var col_white = '#FFFFFF';// aka rgb 255 255 255
var col_black = '#000000';// aka rgb 000 000 000
var col_background = '#808080';// aka rgb 128 128 128
var col_confBar = '#E5E5E5';// aka rbg 229 229 229
var col_green = '#00FF00';// aka rgb 0 255 0
var col_red = '#FF0000'; //aka rgb 255 0 0
var col_bucket = '#FF8000'; //aka 255 128 0
var col_money = '#FFE500'; //aka rgb 255 299 0
var col_iconFront = '#FCE300' ;// 252 227 0
var col_iconBack = '#FAA500'; //250 165 0
// Open window and window data
var canvas_width = 800;
var canvas_height = 600;
var canvas_xCenter = canvas_width/2;
var canvas_yCenter = canvas_height/2;
//Time variables
//var time_deadLine = 2500; // deadline for response
var time_ISI = 800; // time between confirm and dot shoot
var time_RSI = 500; // time between two key presses
var time_ITI = 800; // intertrialinterval
// var time_EBI = 1000; // end block interval
var time_dotSpeed = 500; // how many frames to reach other side? (seconds -> frames)
// Stimulus variables
var stim_std = 12; //standard deviation for particle landing
var stim_staHazRate = 0.025; //4 changes in 150 trials (or 3, because the first one signals change)
// var stim_midHazRate = 0.08; //eight changes in 100 trials
var stim_volHazRate = 0.125; //19 changes in 150 trials (or 18, because the first one signals change)
var stim_pracHazRate = 0.01; //hazard rate for the practice phase
var stim_cirDia = 500; // diameter of big circle (every degree = 4 px)
var stim_cirThickness = 15; //thickness of circle
var stim_cirNPos = 360; // number of possible end points on circle
var stim_innerCirDia = stim_cirDia-stim_cirThickness; //to draw inner circle to get a ring
var stim_dotDia = 8; // radius (or dia?) of money dots (in px?)
var stim_bucketDepth = 50; //thickness of bucket
var stim_bucketDia = stim_cirDia + stim_bucketDepth; //diameter of bucket
var stim_bucketSize = stim_std*3; //bucket is 3 sd of distribution //this is in degrees
var stim_bucketSpeed = 1.5;//was 2
var stim_fixDia = 9; //dia of the fixation circle
var stim_moneyDia = 20; //diameter of coin
var stim_moneyAmt = 10; //how much win or lose per trial
var stim_coord = circleCoor(stim_cirNPos, canvas_xCenter, canvas_yCenter, (stim_cirDia-stim_dotDia)/2);
//confidence bar variables
var conf_barHeight = 20;
var conf_barWidth = 500;
var conf_barSpeed = 1;//was 2
var conf_indRand = [25,75];
var conf_indHeight = conf_barHeight; //for the indicatior
var conf_indWidth = conf_barWidth/100;
var conf_range = [1,100]; // min and max value of conf?
var conf_check =[]; //this is for the array to check if they moved their confidence or not
var conf_checkTrial = 20; //how many trials before checking how many they kept choosing the same confidence
var conf_checkTrial2 = 50; //second check of their confidence levels at trial 50
// THIS IS TO CALCULATE CPP AND PRESET PARTICLE LOCATION (IN DEGREES) FOR STABLE AND VOLATILE BLOCKS
var nCond = set_cond.length;
var nTrialsPerCond = 150;//150
var nTrialsPrac = 10;//10
var nTrialsTotal = nTrialsPerCond*nCond;
var nBlocks = 5;//4
var nTrialsPerBlock= Math.floor(nTrialsTotal/nBlocks); //this has to be divisible by nBlocks
////////////////////////
//data variables that will change with every trial
var data_expStart = -1; // start of experiment
var data_expEnd = -1; // end of experiment
var data_expDur = -1; // duration of experiment
var data_trialStart = -1; // start of trial
var data_trialEnd = -1; // end of trial
var data_trialDur = -1; // duration of trial
var data_nTrial = 0; //index for overall trial number
var data_nTrialPrac = 0; //index for practice trial number
var data_nBlock = 1; //index for block number
var data_nTrialInBlock = 0; //index for trial number per resting block (resets)
var data_nTrialInCond = 0; //index for trial number in one condition block
var data_nCond = -1; //index for condition block number
var data_condition = -1; //index for stable or volatile block
// var data_refLoc = -1; //index where particle actually shoots to
var data_changePoint = -1; // index if particle is at a change point trial
var data_buckPos = Math.floor(Math.random()*stim_cirNPos); //where bucket position was chosen //it is random on trial 1
var data_buckPosRT = -1; //how long to choose bucket postion
var data_conf = -1; //confidence rating
var data_confRTStart = -1; //conf start time
var data_confRTEnd = -1; //conf end time
var data_confRT = -1; //how long to set confidence
var data_hitMiss = -1; //basically accuracy
var data_trialReward = -1; //gain or lose per trial
// var data_blockReward = -1; //total gain or lose for particular block
var data_totalReward = 0; //running total of how many coins (start with 0?)
//this is to append to datastring?
var data = {
nTrial : -1, //index for overall trial number
trialStart : -1, // start of trial
trialEnd : -1, // end of trial
trialDur : -1, // duration of trial
nCond : -1, //index for condition block number
condition : -1, //index for stable or volatile block
nTrialInCond : -1, //index for trial number in one condition block
nBlock : -1, //index for block number
nTrialinBlock : -1, //index for trial number per resting block (resets)
hazRate : -1, //index for hazRate set...
refLocMean : -1, //index where particle mean
refLoc : -1, //index where particle actually shoots to
changePoint : -1, //whether it is a change point trial or not
buckPos : -1, //where bucket position was chosen
buckPosRT : -1, //how long to choose bucket postion
conf : -1, //confidence rating
confRTStart : -1, //conf start time
confRT : -1, //how long to set confidence
hitMiss : -1, //basically accuracy
trialReward : -1, //gain or lose per trial
// blockReward : -1, //total gain or lose for particular block
totalReward : 0, //running total of how many coins (start with 0?)
confOri : -1, //this is the default conf 25/75
reset : -1, //this is to see how many times they had to reset the expt because of confidence
to_array: function() {
var arr = [];
var _this = this;
Object.keys(this).forEach(function (key) {
if(key != 'to_array'){
arr.push(_this[key]);
}
});
return arr;
}
};
//confidence variables - these need to be global because of drawing the indicator in global space
var newConf = shuffle(conf_indRand)[0];//calculating new x pos for confidence bar position, this returns it to 50 on every trial
var prevConf = newConf;
var conf_pos = confConvert(canvas_xCenter,conf_barWidth,newConf);
showpage('test'); //this is the empty html
/// functions to draw the task stimuli
//functions like drawing circle...
//this one will draw the ring and the fixation dot all in white
function draw_defaultCircle(canvas_xCenter, canvas_yCenter, stim_cirDia, stim_innerCirDia, stim_dotDia, col_white, col_background) {
var outerCircle = paper.circle(canvas_xCenter, canvas_yCenter, stim_cirDia / 2).attr({
stroke: col_white,
fill: col_white
});
var innercircle = paper.circle(canvas_xCenter, canvas_yCenter, stim_innerCirDia / 2).attr({
stroke: col_white,
fill: col_background
});
var fixDot = paper.circle(canvas_xCenter, canvas_yCenter, stim_dotDia / 2).attr({
stroke: col_white,
fill: col_white
});
return fixDot;
}
//this will draw the bucket both the orange ring and grey out the white ring for that part as well
//bucket pos is the degree where the bucket position starts from... 0 to 359
function draw_bucket(canvas_xCenter, canvas_yCenter, stim_cirDia, stim_innerCirDia, stim_bucketDia, stim_bucketSize, col_bucket, col_background, bucket_pos) {
//the inputs are in degrees
//this is for the orange part
var bucket1 = paper.path().attr({
stroke: col_bucket,
fill: col_bucket,
"stroke-width": 1,
arc: [canvas_xCenter, canvas_yCenter, bucket_pos, bucket_pos + stim_bucketSize, stim_cirDia / 2, stim_bucketDia / 2]
});
//this is for the grey part
var bucket2 = paper.path().attr({
stroke: col_background,
fill: col_background,
"stroke-width": 2,
arc: [canvas_xCenter, canvas_yCenter, bucket_pos, bucket_pos + stim_bucketSize, stim_innerCirDia / 2, stim_cirDia / 2]
});
return {
bucket1:bucket1,
bucket2: bucket2
};
}
//this will draw the confidence box
function draw_confBar(canvas_xCenter, canvas_height, conf_barWidth, conf_barHeight,col_confBar, col_black) {
//confidence box
var confBar = paper.rect(canvas_xCenter - (conf_barWidth / 2), canvas_height + 10, conf_barWidth, conf_barHeight).attr({
stroke: col_black,
"stroke-width": 3,
fill: col_confBar
});
//confidence text
var confBegin = paper.text(canvas_xCenter - (conf_barWidth / 2), canvas_height - 5, "1").attr({
stroke: col_black,
"font-size": 25,
fill: col_black
});
var confEnd = paper.text(canvas_xCenter + (conf_barWidth / 2), canvas_height - 5, "100").attr({
stroke: col_black,
"font-size": 25,
fill: col_black
});
return {confBar:confBar,
confBegin:confBegin,
confEnd:confEnd
};
}
//this will draw the confIndicator (this is the thing that will need to redraw....)
//confidence indicator..conf_pos is where the indicator is placed, in dot coord. middle is canvas_xCenter
function draw_confInd(canvas_height, conf_indWidth, conf_indHeight, col_bucket, conf_pos) {
var confInd = paper.rect(conf_pos-(conf_indWidth/2), canvas_height + 10, conf_indWidth, conf_indHeight).attr({
stroke: col_bucket,
"stroke-width": 2,
fill: col_bucket
});
return confInd;
}
//moneyAmt should be a string saying how much points they have currently
function draw_confText(canvas_height, canvas_width,col_black) {
var confText = paper.text(canvas_width/2, canvas_height-10, "Rate your confidence").attr({
stroke: col_black,
"font-size": 18,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
return confText;
}
//this will draw the money indicator
//money indicator (score beside coin icon)
function draw_moneyCoin(canvas_width, stim_moneyDia, col_iconFront, col_iconBack) {
var money1 = paper.circle(canvas_width - 110, 50, stim_moneyDia / 2).attr({
stroke: col_iconBack,
"stroke-width": 2,
fill: col_iconFront
});
var money2 = paper.circle(canvas_width - 100, 38, stim_moneyDia / 2).attr({
stroke: col_iconBack,
"stroke-width": 2,
fill: col_iconFront
});
}
//moneyAmt should be a string saying how much points they have currently
function draw_moneyText(canvas_width, col_black, money_Amt) {
var moneyInd = paper.text(canvas_width - 52, 44, money_Amt).attr({
stroke: col_black,
"font-size": 30,
fill: col_black
});
return moneyInd;
}
//function to animate time_dotSpeed
//dot_pos is the degree where the dot will be shot
function draw_dotShoot(canvas_xCenter, canvas_yCenter, stim_coord, stim_cirDia, stim_dotDia, col_money, time_dotSpeed, dot_pos) {
//particles shooting out
var particle = paper.circle(canvas_xCenter, canvas_yCenter, stim_dotDia / 2).attr({
stroke: col_money,
fill: col_money
});
//cx and cy are where the dot lands in the end, inputs is pix position, not degrees from circle..
//ill need something to convert degrees into pix position, depending on area of circle?
// particle.animate( {cx:stim_coord.x[dot_pos], cy: stim_coord.y[dot_pos] }, time_dotSpeed, "linear", function() {particle.animate({opacity: 0 }, 10); }); //this makes sure the particle goes to zero opacity in 10 seconds
particle.animate({
cx: stim_coord.x[dot_pos],
cy: stim_coord.y[dot_pos]
}, time_dotSpeed, "linear", function() {
particle.remove();
}); //this makes the particle disappear (remove element...)
}
function draw_pracConfirmText(canvas_height, canvas_width,col_black) {
var pracConfirmText = paper.text(canvas_width/2, canvas_height/2+80, "Press the spacebar to confirm its position.").attr({
stroke: col_black,
"font-size": 15,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
return pracConfirmText;
}
function draw_pracConfText(canvas_height, canvas_width,col_black) {
var pracConfText1 = paper.text(canvas_width/2, canvas_height/2+40, "Press the right arrow key to move \nthe confidence indicator rightward.").attr({
stroke: col_black,
"font-size": 15,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
var pracConfText2 = paper.text(canvas_width/2, canvas_height/2-40, "Press the left arrow key to move \nthe confidence indicator leftward.").attr({
stroke: col_black,
"font-size": 15,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
return {pracConfText1:pracConfText1,
pracConfText2:pracConfText2
};
}
function draw_pracText(canvas_height, canvas_width,col_black) {
var pracText1 = paper.text(canvas_width/2, canvas_height/2+40, "Press the right arrow key to \nmove the bucket clockwise.").attr({
stroke: col_black,
"font-size": 15,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
var pracText2 = paper.text(canvas_width/2, canvas_height/2-40, "Press the left arrow key to \nmove the bucket counter-clockwise.").attr({
stroke: col_black,
"font-size": 15,
"font-family":" Verdana, Helvetica, sans-serif",
fill: col_black
});
return {pracText1:pracText1,
pracText2:pracText2
};
}
// function to caluclate experiment condition
function stim_locVariables(nTrialsPerCond,stim_HazRate){
// where the dots will land is precaluclated beore experiment starts and positions are all stored in the vars below
var stim_refLocMean=[]; //where the current mean is
var stim_locDiff =[]; //the deviation from mean of 0
var nChangePoint = Math.round(nTrialsPerCond*stim_HazRate);
//this is if with very little trials and low H rate, the code will break bc array cannot be made
if (nChangePoint<1){
var nChangePointFin = 1; //ensures there is at least one changepoint (the starting one)
}else{
var nChangePointFin = nChangePoint;
}
var Ntrial = Array(nTrialsPerCond-nChangePoint).fill(0);
var CPtrial =Array(nChangePointFin-1).fill(1);
var stim_changePointTemp = shuffle(CPtrial.concat(Ntrial));
var stim_changePoint = [1].concat(stim_changePointTemp);
for (t = 0; t < nTrialsPerCond; t++){
stim_locDiff = stim_locDiff.concat(Math.round(Math.randomGaussian(0,stim_std))); //this is with the math.gaus library
if (stim_changePoint[t] == 1) {
var currLoc = Math.round((stim_cirNPos-1)*Math.random()); //its ok to be zero? since its 0 to 359 here
stim_refLocMean = stim_refLocMean.concat(currLoc);
}else{
currLoc = Math.round(stim_refLocMean[t-1]);
stim_refLocMean=stim_refLocMean.concat(currLoc);
}
}
// Put samples around reference location
var stim_refLoc = stim_refLocMean.SumArray(stim_locDiff); // adding the 12 sd random pick to mean
//this is to ensure if loc goes negative, it still spits out the correct location
var stim_refLocFin = [];
for (w=0; w<stim_refLoc.length; w++)
{
var LocTemp = mod(stim_refLoc[w],stim_cirNPos);
stim_refLocFin= stim_refLocFin.concat(LocTemp);
}
return {
stim_refLocMean: stim_refLocMean,
stim_refLocFin: stim_refLocFin,
stim_changePoint: stim_changePoint
};
}
//KEYBOARD RESPONSES (THIS IS CODED WITH KEYDROWN JS TO REMOVE THE KEYBOARD DELAY REPEAT ISSUE)
//IF PRESS LEFT ARROW KEY
kd.LEFT.down(function () {
if (buckListening){ //THIS IS FOR BUCKET RESPONSE
buck_move = -stim_bucketSpeed; //if bucketSpeed is 2, then it moves 2deg each time the key is pressed
data_buckPos = Math.round(data_buckPos + buck_move);
if (data_buckPos > 360 || data_buckPos <= 0) { //making sure the bucket stayed between 0 and 359
data_buckPos = mod(data_buckPos,stim_cirNPos);
}else {
data_buckPos = data_buckPos;
}
bucket.bucket1.remove();
bucket.bucket2.remove();
bucket = draw_bucket (canvas_xCenter,canvas_yCenter, stim_cirDia, stim_innerCirDia,stim_bucketDia,stim_bucketSize,col_bucket,col_background,data_buckPos);
} else if (confListening){ //THIS IS FOR CONFIDENCE RESPONSE
conf_move = -conf_barSpeed;
newConf = prevConf + conf_move;
if (newConf < 1 || newConf > 100) { //making sure the conf doesnt go out of bounds
newConf = prevConf;
} else {
prevConf = newConf;
}
var data_conf = newConf;
conf_pos = confConvert(canvas_xCenter,conf_barWidth,data_conf);
//conf_pos is the dot coordinate
//need to convert conf into dot coordinate
confInd.remove();
confInd = draw_confInd(canvas_height, conf_indWidth, conf_indHeight, col_bucket, conf_pos); //draw final conf rating
data.conf = data_conf;
} else {
// console.log("ERROR AT PRESSING LEFT");
return false;
}
});
//IF PRESS right ARROW KEY
kd.RIGHT.down(function () {
if (buckListening){
buck_move = stim_bucketSpeed; //if bucketSpeed is 2, then it moves 2deg each time the key is pressed
data_buckPos = Math.round(data_buckPos + buck_move);
if (data_buckPos > 360 || data_buckPos <= 0) {
data_buckPos = mod(data_buckPos,stim_cirNPos);
}else {
data_buckPos = data_buckPos;
}
bucket.bucket1.remove();
bucket.bucket2.remove();
bucket = draw_bucket (canvas_xCenter,canvas_yCenter, stim_cirDia, stim_innerCirDia,stim_bucketDia,stim_bucketSize,col_bucket,col_background,data_buckPos);
} else if (confListening){
conf_move = conf_barSpeed;
newConf = prevConf + conf_move;
if (newConf < 1 || newConf > 100) { //making sure the conf doesnt go out of bounds
newConf = prevConf;
} else {
prevConf = newConf;
}
var data_conf = newConf;
conf_pos = confConvert(canvas_xCenter,conf_barWidth,data_conf);
//conf_pos is the dot coordinate
//need to convert conf into dot coordinate
confInd.remove();
confInd = draw_confInd(canvas_height, conf_indWidth, conf_indHeight, col_bucket, conf_pos); //draw final conf rating
data.conf = data_conf;
}else{
// console.log("ERROR AT PRESSING RIGHT");
return false;
}
});
//this is space bar PRESS (ie key has to be released before response is made)
kd.SPACE.press(function () {
if (buckListening){
buckListening = false;
data_buckPosRT= new Date().getTime() - data_trialStart - data_expStart;
data.buckPos = data_buckPos;
data.buckPosRT = data_buckPosRT;
// console.log("Bucketposition is " +data.buckPos);
setTimeout(function() {
draw_conf();
}, time_RSI); }
else if (confListening){
confListening = false;
data_confRTEnd = new Date().getTime() - data_expStart;
data_confRT = data_confRTEnd - data_confRTStart;
data.confRT = data_confRT;
// console.log("Confposition is " +data.conf);
setTimeout(function() {
draw_dot();
}, time_ISI);
}
else if (progListening){ //this is for the breaks
progPage.remove();
progPageText.remove();
progListening = false;
if (last_block){
questSection();
}else{
start_expt();
}
}
else{
// console.log("ERROR AT PRESSING SPACE");
return false;
}
});
// draw the confidence scale bar
var draw_conf = function(){
if (is_training){
pracText.pracText1.remove();
pracText.pracText2.remove();
pracConfText = draw_pracConfText(canvas_height,canvas_width,col_black);}
//start of confidence rating
data_confRTStart = new Date().getTime() - data_expStart;
data.confRTStart = data_confRTStart;
data.confOri = shuffle(conf_indRand)[0]; //here is to code for the conf that is randomly presented
prevConf = data.confOri; //this is to reset confidence indicator to either 25 or 75 after every trial
conf_pos = confConvert(canvas_xCenter,conf_barWidth,prevConf);
confInd = draw_confInd(canvas_height, conf_indWidth, conf_indHeight, col_bucket, conf_pos); //if i dont decalre var, this is a global variable, hence i can remove or hide it in the other functions
confText = draw_confText(canvas_height,canvas_width,col_black);
confBar.confBar.show();
confBar.confBegin.show();
confBar.confEnd.show();
data.conf = prevConf;//this is needed bc of the randomisation of the conf, so its 25 if 75 if dont move
confListening = true;
};
//draw particles flying to the edge
var draw_dot = function(){
//this gives the list of degrees on the circle that the bucket will catch the particle.
var data_buckRangeTemp = numberRange(data_buckPos,data_buckPos+stim_bucketSize);
var data_buckRange = data_buckRangeTemp.map(function(element) {
return mod(element,360);
});
var dot_pos =-1;
var dot_Mpos =-1;
//this is calculate if the dot will hit the bucket