@decidables/detectable-elements 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/LICENSE.md +1112 -0
- package/README.md +1218 -0
- package/lib/detectableElements.esm.js +18385 -0
- package/lib/detectableElements.esm.js.map +1 -0
- package/lib/detectableElements.esm.min.js +13 -0
- package/lib/detectableElements.esm.min.js.map +1 -0
- package/lib/detectableElements.umd.js +18413 -0
- package/lib/detectableElements.umd.js.map +1 -0
- package/lib/detectableElements.umd.min.js +13 -0
- package/lib/detectableElements.umd.min.js.map +1 -0
- package/package.json +58 -0
- package/src/components/detectable-control.js +272 -0
- package/src/components/detectable-response.js +414 -0
- package/src/components/detectable-table.js +602 -0
- package/src/components/index.js +7 -0
- package/src/components/rdk-task.js +586 -0
- package/src/components/roc-space.js +1220 -0
- package/src/components/sdt-model.js +1835 -0
- package/src/detectable-element.js +121 -0
- package/src/equations/dc2far.js +182 -0
- package/src/equations/dc2hr.js +191 -0
- package/src/equations/facr2far.js +120 -0
- package/src/equations/hm2hr.js +121 -0
- package/src/equations/hmfacr2acc.js +161 -0
- package/src/equations/hrfar2c.js +179 -0
- package/src/equations/hrfar2d.js +162 -0
- package/src/equations/index.js +8 -0
- package/src/equations/sdt-equation.js +141 -0
- package/src/examples/double-interactive.js +171 -0
- package/src/examples/human.js +184 -0
- package/src/examples/index.js +6 -0
- package/src/examples/interactive.js +131 -0
- package/src/examples/model.js +203 -0
- package/src/examples/sdt-example.js +76 -0
- package/src/examples/unequal.js +43 -0
- package/src/index.js +6 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
|
|
2
|
+
import {html, css} from 'lit';
|
|
3
|
+
|
|
4
|
+
import '@decidables/decidables-elements/button';
|
|
5
|
+
|
|
6
|
+
import DetectableElement from '../detectable-element';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
DetectableResponse element
|
|
10
|
+
<detectable-response>
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
|
|
14
|
+
*/
|
|
15
|
+
export default class DetectableResponse extends DetectableElement {
|
|
16
|
+
static get properties() {
|
|
17
|
+
return {
|
|
18
|
+
feedback: {
|
|
19
|
+
attribute: 'feedback',
|
|
20
|
+
type: String,
|
|
21
|
+
reflect: true,
|
|
22
|
+
},
|
|
23
|
+
trial: {
|
|
24
|
+
attribute: 'trial',
|
|
25
|
+
type: Boolean,
|
|
26
|
+
reflect: true,
|
|
27
|
+
},
|
|
28
|
+
payoff: {
|
|
29
|
+
attribute: 'payoff',
|
|
30
|
+
type: String,
|
|
31
|
+
reflect: true,
|
|
32
|
+
},
|
|
33
|
+
hPayoff: {
|
|
34
|
+
attribute: 'hit-payoff',
|
|
35
|
+
type: Number,
|
|
36
|
+
reflect: true,
|
|
37
|
+
},
|
|
38
|
+
mPayoff: {
|
|
39
|
+
attribute: 'miss-payoff',
|
|
40
|
+
type: Number,
|
|
41
|
+
reflect: true,
|
|
42
|
+
},
|
|
43
|
+
faPayoff: {
|
|
44
|
+
attribute: 'false-alarm-payoff',
|
|
45
|
+
type: Number,
|
|
46
|
+
reflect: true,
|
|
47
|
+
},
|
|
48
|
+
crPayoff: {
|
|
49
|
+
attribute: 'correct-rejection-payoff',
|
|
50
|
+
type: Number,
|
|
51
|
+
reflect: true,
|
|
52
|
+
},
|
|
53
|
+
nrPayoff: {
|
|
54
|
+
attribute: 'no-response-payoff',
|
|
55
|
+
type: Number,
|
|
56
|
+
reflect: true,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
state: {
|
|
60
|
+
attribute: false,
|
|
61
|
+
type: String,
|
|
62
|
+
reflect: false,
|
|
63
|
+
},
|
|
64
|
+
trialCount: {
|
|
65
|
+
attribute: false,
|
|
66
|
+
type: Number,
|
|
67
|
+
reflect: false,
|
|
68
|
+
},
|
|
69
|
+
trialTotal: {
|
|
70
|
+
attribute: false,
|
|
71
|
+
type: Number,
|
|
72
|
+
reflect: false,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
constructor() {
|
|
78
|
+
super();
|
|
79
|
+
|
|
80
|
+
// Attributes
|
|
81
|
+
this.feedbacks = ['none', 'accuracy', 'outcome']; // Possible values for 'feedback'
|
|
82
|
+
this.feedback = 'outcome'; // What feedback to display
|
|
83
|
+
this.trial = false; // Show trial count?
|
|
84
|
+
this.payoffs = ['none', 'trial', 'total']; // Possible types of 'payoff' info
|
|
85
|
+
this.payoff = 'none'; // What payoff info to display
|
|
86
|
+
|
|
87
|
+
this.hPayoff = 0; // Hit payoff
|
|
88
|
+
this.mPayoff = 0; // Miss payoff
|
|
89
|
+
this.crPayoff = 0; // Correct Rejection payoff
|
|
90
|
+
this.faPayoff = 0; // False Alarm payoff
|
|
91
|
+
this.nrPayoff = 0; // No Response payoff
|
|
92
|
+
|
|
93
|
+
// Properties
|
|
94
|
+
this.states = ['off', 'waiting', 'feedback']; // Possible states
|
|
95
|
+
this.state = 'off'; // Current state
|
|
96
|
+
|
|
97
|
+
this.trialCount = 0; // Current trial
|
|
98
|
+
this.trialTotal = 0; // Total trials
|
|
99
|
+
|
|
100
|
+
// Private
|
|
101
|
+
this.signals = ['present', 'absent']; // Possible values of 'signal'
|
|
102
|
+
this.signal = undefined; // Signal for current trial
|
|
103
|
+
this.responses = ['present', 'absent']; // Possible values of 'response'
|
|
104
|
+
this.response = undefined; // Response for current trial
|
|
105
|
+
this.outcomes = ['h', 'm', 'fa', 'cr', 'nr']; // Possible values of 'outcome'
|
|
106
|
+
this.outcome = undefined; // Outcome for current trial
|
|
107
|
+
this.accuracies = ['c', 'e', 'nr']; // Possible values of 'accuracy'
|
|
108
|
+
this.accuracy = undefined; // Accuracy for current trial
|
|
109
|
+
|
|
110
|
+
this.h = 0; // Count of Hits
|
|
111
|
+
this.m = 0; // Count of Misses
|
|
112
|
+
this.cr = 0; // Count of Correct Rejections
|
|
113
|
+
this.fa = 0; // Count of False Alarms
|
|
114
|
+
|
|
115
|
+
this.c = 0; // Count of Correct trials
|
|
116
|
+
this.e = 0; // Count of Error trials
|
|
117
|
+
|
|
118
|
+
this.nr = 0; // Count of No Response trials
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get trialPayoff() {
|
|
122
|
+
switch (this.outcome) {
|
|
123
|
+
case 'h':
|
|
124
|
+
return this.hPayoff;
|
|
125
|
+
case 'm':
|
|
126
|
+
return this.mPayoff;
|
|
127
|
+
case 'fa':
|
|
128
|
+
return this.faPayoff;
|
|
129
|
+
case 'cr':
|
|
130
|
+
return this.crPayoff;
|
|
131
|
+
case 'nr':
|
|
132
|
+
return this.nrPayoff;
|
|
133
|
+
default:
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get totalPayoff() {
|
|
139
|
+
return ((this.h * this.hPayoff)
|
|
140
|
+
+ (this.m * this.mPayoff)
|
|
141
|
+
+ (this.cr * this.crPayoff)
|
|
142
|
+
+ (this.fa * this.faPayoff)
|
|
143
|
+
+ (this.nr * this.nrPayoff));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
start(signal, trial) {
|
|
147
|
+
this.trialCount = trial;
|
|
148
|
+
this.state = 'waiting';
|
|
149
|
+
this.signal = signal;
|
|
150
|
+
this.response = undefined;
|
|
151
|
+
this.outcome = undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
stop() {
|
|
155
|
+
this.state = 'feedback';
|
|
156
|
+
if (this.response === undefined) {
|
|
157
|
+
this.outcome = 'nr';
|
|
158
|
+
this.nr += 1;
|
|
159
|
+
this.accuracy = 'nr';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
present() {
|
|
164
|
+
this.responded('present');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
absent() {
|
|
168
|
+
this.responded('absent');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
responded(response) {
|
|
172
|
+
this.state = 'feedback';
|
|
173
|
+
this.response = response;
|
|
174
|
+
if (this.signal === 'present' && this.response === 'present') {
|
|
175
|
+
this.outcome = 'h';
|
|
176
|
+
this.h += 1;
|
|
177
|
+
this.accuracy = 'c';
|
|
178
|
+
this.c += 1;
|
|
179
|
+
} else if (this.signal === 'present' && this.response === 'absent') {
|
|
180
|
+
this.outcome = 'm';
|
|
181
|
+
this.m += 1;
|
|
182
|
+
this.accuracy = 'e';
|
|
183
|
+
this.e += 1;
|
|
184
|
+
} else if (this.signal === 'absent' && this.response === 'present') {
|
|
185
|
+
this.outcome = 'fa';
|
|
186
|
+
this.fa += 1;
|
|
187
|
+
this.accuracy = 'e';
|
|
188
|
+
this.e += 1;
|
|
189
|
+
} else if (this.signal === 'absent' && this.response === 'absent') {
|
|
190
|
+
this.outcome = 'cr';
|
|
191
|
+
this.cr += 1;
|
|
192
|
+
this.accuracy = 'c';
|
|
193
|
+
this.c += 1;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.dispatchEvent(new CustomEvent('detectable-response', {
|
|
197
|
+
detail: {
|
|
198
|
+
trial: this.trialCount,
|
|
199
|
+
signal: this.signal,
|
|
200
|
+
response: this.response,
|
|
201
|
+
outcome: this.outcome,
|
|
202
|
+
payoff: this.trialPayoff,
|
|
203
|
+
h: this.h,
|
|
204
|
+
m: this.m,
|
|
205
|
+
fa: this.fa,
|
|
206
|
+
cr: this.cr,
|
|
207
|
+
nr: this.nr,
|
|
208
|
+
totalPayoff: this.totalPayoff,
|
|
209
|
+
},
|
|
210
|
+
bubbles: true,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
reset() {
|
|
215
|
+
this.state = 'off';
|
|
216
|
+
this.trialCount = 0;
|
|
217
|
+
this.signal = undefined;
|
|
218
|
+
this.response = undefined;
|
|
219
|
+
this.outcome = undefined;
|
|
220
|
+
this.accuracy = undefined;
|
|
221
|
+
this.h = 0;
|
|
222
|
+
this.m = 0;
|
|
223
|
+
this.cr = 0;
|
|
224
|
+
this.fa = 0;
|
|
225
|
+
this.nr = 0;
|
|
226
|
+
this.c = 0;
|
|
227
|
+
this.e = 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static get styles() {
|
|
231
|
+
return [
|
|
232
|
+
super.styles,
|
|
233
|
+
css`
|
|
234
|
+
:host {
|
|
235
|
+
display: inline-block;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Overall container */
|
|
239
|
+
.holder {
|
|
240
|
+
display: flex;
|
|
241
|
+
|
|
242
|
+
flex-direction: row;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Response buttons */
|
|
246
|
+
.responses {
|
|
247
|
+
display: flex;
|
|
248
|
+
|
|
249
|
+
flex-direction: column;
|
|
250
|
+
|
|
251
|
+
align-items: stretch;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.waiting[disabled] {
|
|
256
|
+
--decidables-button-background-color: var(---color-element-enabled);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.selected[disabled][name="present"] {
|
|
260
|
+
--decidables-button-background-color: var(---color-present);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.selected[disabled][name="absent"] {
|
|
264
|
+
--decidables-button-background-color: var(---color-absent);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Feedback messages */
|
|
268
|
+
.feedbacks {
|
|
269
|
+
display: flex;
|
|
270
|
+
|
|
271
|
+
flex-direction: column;
|
|
272
|
+
|
|
273
|
+
justify-content: center;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Trial feedback */
|
|
277
|
+
.trial {
|
|
278
|
+
text-align: center;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.trial .label {
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* Outcome feedback */
|
|
286
|
+
.feedback {
|
|
287
|
+
display: flex;
|
|
288
|
+
|
|
289
|
+
flex-direction: column;
|
|
290
|
+
|
|
291
|
+
align-items: center;
|
|
292
|
+
justify-content: center;
|
|
293
|
+
|
|
294
|
+
width: 6rem;
|
|
295
|
+
height: 3.5rem;
|
|
296
|
+
padding: 0.375rem 0.75rem;
|
|
297
|
+
margin: 0.25rem;
|
|
298
|
+
|
|
299
|
+
text-align: center;
|
|
300
|
+
|
|
301
|
+
background-color: var(---color-element-background);
|
|
302
|
+
border: 1px solid var(---color-element-border);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.feedback.h {
|
|
306
|
+
background-color: var(---color-h-light);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.feedback.m {
|
|
310
|
+
background-color: var(---color-m-light);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.feedback.fa {
|
|
314
|
+
background-color: var(---color-fa-light);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.feedback.cr {
|
|
318
|
+
background-color: var(---color-cr-light);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.feedback.nr {
|
|
322
|
+
background-color: var(---color-nr-light);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.feedback.c {
|
|
326
|
+
background-color: var(---color-correct-light);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.feedback.e {
|
|
330
|
+
color: var(---color-text-inverse);
|
|
331
|
+
|
|
332
|
+
background-color: var(---color-error-light);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.feedback .outcome {
|
|
336
|
+
font-weight: 600;
|
|
337
|
+
line-height: 1.15;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
:host([payoff="trial"]) .feedback,
|
|
341
|
+
:host([payoff="total"]) .feedback {
|
|
342
|
+
height: 4rem;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* Payoff feedback */
|
|
346
|
+
.payoff {
|
|
347
|
+
text-align: center;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.payoff .label {
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
}
|
|
353
|
+
`,
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
render() {
|
|
358
|
+
return html`
|
|
359
|
+
<div class="holder">
|
|
360
|
+
<div class="responses">
|
|
361
|
+
<decidables-button name="present" class=${(this.state === 'feedback' && this.response === 'present') ? 'selected' : ((this.state === 'waiting') ? 'waiting' : '')} ?disabled=${this.state !== 'waiting' || this.interactive !== true} @click=${this.present.bind(this)}>Present</decidables-button>
|
|
362
|
+
<decidables-button name="absent" class=${(this.state === 'feedback' && this.response === 'absent') ? 'selected' : ((this.state === 'waiting') ? 'waiting' : '')} ?disabled=${this.state !== 'waiting' || this.interactive !== true} @click=${this.absent.bind(this)}>Absent</decidables-button>
|
|
363
|
+
</div>
|
|
364
|
+
${(this.trial || this.feedback !== 'none' || this.payoff === 'total')
|
|
365
|
+
? html`
|
|
366
|
+
<div class="feedbacks">
|
|
367
|
+
${(this.trial)
|
|
368
|
+
? html`
|
|
369
|
+
<div class="trial">
|
|
370
|
+
<span class="label">Trial: </span><span class="count">${this.trialCount}</span><span class="of"> of </span><span class="total">${this.trialTotal}</span>
|
|
371
|
+
</div>`
|
|
372
|
+
: html``}
|
|
373
|
+
${(this.feedback !== 'none')
|
|
374
|
+
? html`
|
|
375
|
+
<div class=${`feedback ${(this.state === 'feedback')
|
|
376
|
+
? (this.feedback === 'outcome')
|
|
377
|
+
? this.outcome
|
|
378
|
+
: this.accuracy
|
|
379
|
+
: ''}`}>
|
|
380
|
+
${(this.state === 'feedback')
|
|
381
|
+
? (this.feedback === 'outcome')
|
|
382
|
+
? (this.outcome === 'h')
|
|
383
|
+
? html`<span class="outcome">Hit</span>`
|
|
384
|
+
: (this.outcome === 'm')
|
|
385
|
+
? html`<span class="outcome">Miss</span>`
|
|
386
|
+
: (this.outcome === 'fa')
|
|
387
|
+
? html`<span class="outcome">False<br>Alarm</span>`
|
|
388
|
+
: (this.outcome === 'cr')
|
|
389
|
+
? html`<span class="outcome">Correct<br>Rejection</span>`
|
|
390
|
+
: html`<span class="outcome">No<br>Response</span>`
|
|
391
|
+
: (this.accuracy === 'c')
|
|
392
|
+
? html`<span class="outcome">Correct</span>`
|
|
393
|
+
: (this.accuracy === 'e')
|
|
394
|
+
? html`<span class="outcome">Error</span>`
|
|
395
|
+
: html`<span class="outcome">No<br>Response</span>`
|
|
396
|
+
: ''}
|
|
397
|
+
${(this.payoff === 'trial' || this.payoff === 'total')
|
|
398
|
+
? html`<span class="payoff">${this.trialPayoff}</span>`
|
|
399
|
+
: html``}
|
|
400
|
+
</div>`
|
|
401
|
+
: html``}
|
|
402
|
+
${(this.payoff === 'total')
|
|
403
|
+
? html`
|
|
404
|
+
<div class="payoff">
|
|
405
|
+
<span class="label">Total: </span><span class="value">${this.totalPayoff}</span>
|
|
406
|
+
</div>`
|
|
407
|
+
: html``}
|
|
408
|
+
</div>`
|
|
409
|
+
: html``}
|
|
410
|
+
</div>`;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
customElements.define('detectable-response', DetectableResponse);
|