An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Customize


authored by

Yury-Shevchenko and committed by
Teon L Brooks
9276cc2e d4d5668b

+1285 -546
+9
app/assets/common/divingMan.svg
··· 1 + <svg width="228" height="220" viewBox="0 0 228 220" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 2 + <rect width="228" height="220" fill="url(#pattern0)"/> 3 + <defs> 4 + <pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> 5 + <use xlink:href="#image0" transform="translate(0 -0.000449361) scale(0.00380228 0.00394055)"/> 6 + </pattern> 7 + <image id="image0" width="263" height="254" xlink:href="data:image/png;base64,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"/> 8 + </defs> 9 + </svg>
+395 -72
app/components/DesignComponent/CustomDesignComponent.js
··· 8 8 Form, 9 9 Checkbox, 10 10 Image, 11 - Icon 11 + Table 12 12 } from 'semantic-ui-react'; 13 13 import { isNil } from 'lodash'; 14 14 import styles from '../styles/common.css'; ··· 29 29 import hypothesisImage from '../../assets/common/Hypothesis2.png'; 30 30 import { loadProtocol } from '../../utils/labjs/functions'; 31 31 import { readImages } from '../../utils/filesystem/storage'; 32 + import StimuliRow from './StimuliRow'; 32 33 33 34 const CUSTOM_STEPS = { 34 35 OVERVIEW: 'OVERVIEW', 36 + CONDITIONS: 'CONDITIONS', 37 + TRIALS: 'TRIALS', 35 38 PARAMETERS: 'PARAMETERS', 36 39 PREVIEW: 'PREVIEW' 37 40 }; 38 41 39 - // const CUSTOM_STEPS = { 40 - // OVERVIEW: 'OVERVIEW', 41 - // STIMULI: 'STIMULI', 42 - // PARAMETERS: 'PARAMETERS', 43 - // PREVIEW: 'PREVIEW' 44 - // }; 45 - 46 42 const FIELDS = { 47 43 QUESTION: 'Research Question', 48 44 HYPOTHESIS: 'Hypothesis', 49 45 METHODS: 'Methods', 50 - INTRO: 'Experiment Instructions' 46 + INTRO: 'Experiment Instructions', 47 + HELP: 'Instructions for the task screen', 51 48 }; 52 49 53 50 interface Props { ··· 68 65 isPreviewing: boolean; 69 66 description: ExperimentDescription; 70 67 params: ExperimentParameters; 68 + saved: boolean; 71 69 } 72 70 73 71 export default class CustomDesign extends Component<Props, State> { ··· 86 84 isPreviewing: false, 87 85 description: props.description, 88 86 params: props.params, 87 + saved: false, 89 88 }; 90 89 this.handleStepClick = this.handleStepClick.bind(this); 91 90 this.handleStartExperiment = this.handleStartExperiment.bind(this); ··· 104 103 } 105 104 106 105 handleStepClick(step: string) { 106 + this.handleSaveParams(); 107 107 this.setState({ activeStep: step }); 108 108 } 109 109 ··· 127 127 } 128 128 129 129 handleSaveParams() { 130 + this.setState({ 131 + params: { 132 + ...this.state.params, 133 + dateModified: Date.now(), 134 + } 135 + }); 130 136 this.props.experimentActions.setParams(this.state.params); 131 137 this.props.experimentActions.setDescription(this.state.description); 138 + this.props.experimentActions.saveWorkspace(); 139 + this.setState({ saved: true }); 132 140 } 133 141 134 142 renderSectionContent() { 143 + const stimi = [ 144 + {name: 'stimulus1', number: 1}, 145 + {name: 'stimulus2', number: 2}, 146 + {name: 'stimulus3', number: 3}, 147 + {name: 'stimulus4', number: 4}, 148 + ] 135 149 switch (this.state.activeStep) { 136 - case CUSTOM_STEPS.STIMULI: 150 + case CUSTOM_STEPS.CONDITIONS: 137 151 return ( 138 - <Grid 139 - stretched 140 - padded 141 - relaxed="very" 142 - columns="equal" 143 - className={styles.contentGrid} 144 - > 145 - <StimuliDesignColumn 146 - num={1} 147 - {...this.state.params.stimulus1} 148 - onChange={(key, data) => 149 - this.setState({ 150 - params: { 151 - ...this.state.params, 152 - stimulus1: { ...this.state.params.stimulus1, [key]: data } 153 - } 154 - }) 155 - } 156 - /> 157 - <StimuliDesignColumn 158 - num={2} 159 - {...this.state.params.stimulus2} 160 - onChange={(key, data) => 161 - this.setState({ 162 - params: { 163 - ...this.state.params, 164 - stimulus2: { ...this.state.params.stimulus2, [key]: data } 165 - } 166 - }) 167 - } 168 - /> 152 + <Grid> 153 + <Segment basic> 154 + <Header as="h1">Conditions</Header> 155 + <p> 156 + Select the folder with images for each condition and choose the correct response. 157 + </p> 158 + </Segment> 159 + 160 + <Table basic="very"> 161 + 162 + <Table.Header> 163 + <Table.Row className={styles.conditionHeaderRow}> 164 + <Table.HeaderCell className={styles.conditionHeaderRowName}>Condition</Table.HeaderCell> 165 + <Table.HeaderCell>Default Key Response</Table.HeaderCell> 166 + <Table.HeaderCell>Condition Folder</Table.HeaderCell> 167 + </Table.Row> 168 + </Table.Header> 169 + 170 + <Table.Body className={styles.experimentTable}> 171 + {stimi.map(({name, number}) => ( 172 + <StimuliDesignColumn 173 + key={number} 174 + num={number} 175 + {...this.state.params[name]} 176 + numberImages={this.state.params.stimuli.filter(trial => trial.type === number).length} 177 + onChange={async (key, data, changedName) => { 178 + await this.setState({ 179 + params: { 180 + ...this.state.params, 181 + [changedName]: { ...this.state.params[changedName], [key]: data }, 182 + } 183 + }) 184 + let newStimuli = []; 185 + await stimi.map( stimul => { 186 + let dirStimuli = []; 187 + const dir = this.state.params[stimul.name].dir; 188 + if(dir && typeof(dir) !== 'undefined' && dir !== ''){ 189 + dirStimuli = readImages(dir).map(i => ({ 190 + 'dir': dir, 191 + 'filename': i, 192 + 'name': i, 193 + 'condition': this.state.params[stimul.name].title, 194 + 'response': this.state.params[stimul.name].response, 195 + 'phase': 'main', 196 + 'type': stimul.number, 197 + })); 198 + } 199 + newStimuli = newStimuli.concat(...dirStimuli); 200 + }) 201 + this.setState({ 202 + params: { 203 + ...this.state.params, 204 + stimuli: [ ... newStimuli ], 205 + nbTrials: newStimuli.filter(t => t.phase === 'main').length, 206 + nbPracticeTrials: newStimuli.filter(t => t.phase === 'practice').length, 207 + }, 208 + saved: false, 209 + }) 210 + } 211 + } 212 + /> 213 + )) 214 + } 215 + </Table.Body> 216 + </Table> 169 217 </Grid> 170 218 ); 219 + case CUSTOM_STEPS.TRIALS: 220 + return ( 221 + <Grid> 222 + <Segment basic> 223 + <Header as="h1">Trials</Header> 224 + <p> 225 + Edit the name, condition and correct key response of each trial. 226 + </p> 227 + </Segment> 228 + 229 + <Form style={{ alignSelf: 'flex-end' }}> 230 + <Form.Group> 231 + <Form.Select 232 + fluid 233 + selection 234 + label="Order" 235 + value={this.state.params.randomize} 236 + onChange={(event, data) => 237 + this.setState({ 238 + params: { 239 + ...this.state.params, 240 + randomize: data.value 241 + }, 242 + saved: false, 243 + }) 244 + } 245 + placeholder="Response" 246 + options={[{key: 'random', text: 'Random', value: 'random'}, 247 + {key: 'sequential', text: 'Sequential', value: 'sequential'}]} 248 + /> 249 + <Form.Input 250 + label="Total number of experimental trials" 251 + type="number" 252 + value={this.state.params.nbTrials} 253 + onChange={(event, data) => 254 + this.setState({ 255 + params: { 256 + ...this.state.params, 257 + nbTrials: parseInt(data.value) 258 + }, 259 + saved: false, 260 + }) 261 + } 262 + /> 263 + <Form.Input 264 + label="Total number of practice trials" 265 + type="number" 266 + value={this.state.params.nbPracticeTrials} 267 + onChange={(event, data) => 268 + this.setState({ 269 + params: { 270 + ...this.state.params, 271 + nbPracticeTrials: parseInt(data.value) 272 + }, 273 + saved: false, 274 + }) 275 + } 276 + /> 277 + </Form.Group> 278 + </Form> 279 + 280 + <Table basic="very"> 281 + <Table.Header> 282 + <Table.Row className={styles.trialsHeaderRow}> 283 + <Table.HeaderCell className={styles.conditionHeaderRowName}>Name</Table.HeaderCell> 284 + <Table.HeaderCell>Condition</Table.HeaderCell> 285 + <Table.HeaderCell>Correct Key Response</Table.HeaderCell> 286 + <Table.HeaderCell>Trial Type</Table.HeaderCell> 287 + </Table.Row> 288 + </Table.Header> 289 + <Table.Body className={styles.trialsTable}> 290 + 291 + {this.state.params.stimuli && this.state.params.stimuli.map((e,num) => ( 292 + <StimuliRow 293 + key={num} 294 + num={num} 295 + conditions={[1,2,3,4].map(n => this.state.params[`stimulus${n}`].title)} 296 + {...e} 297 + onDelete={(num) => { 298 + const stimuli = this.state.params.stimuli; 299 + stimuli.splice(num, 1); 300 + const nbPracticeTrials = stimuli.filter(s => (s.phase === 'practice')).length; 301 + const nbTrials = stimuli.filter(s => (s.phase === 'main')).length; 302 + this.setState({ 303 + params: { 304 + ...this.state.params, 305 + stimuli: [ ... stimuli ], 306 + nbPracticeTrials, 307 + nbTrials, 308 + }, 309 + saved: false, 310 + }) 311 + }} 312 + onChange={(num, key, data) => { 313 + const stimuli = this.state.params.stimuli; 314 + stimuli[num][key] = data; 315 + let nbPracticeTrials = this.state.params.nbPracticeTrials; 316 + let nbTrials = this.state.params.nbTrials; 317 + if(key === 'phase'){ 318 + nbPracticeTrials = stimuli.filter(s => (s.phase === 'practice')).length; 319 + nbTrials = stimuli.filter(s => (s.phase === 'main')).length; 320 + } 321 + this.setState({ 322 + params: { 323 + ...this.state.params, 324 + stimuli: [ ... stimuli ], 325 + nbPracticeTrials, 326 + nbTrials, 327 + }, 328 + saved: false, 329 + }) 330 + }} 331 + /> 332 + ))} 333 + 334 + </Table.Body> 335 + </Table> 336 + 337 + </Grid> 338 + ); 171 339 case CUSTOM_STEPS.PARAMETERS: 172 340 return ( 173 - <Grid 174 - stretched 175 - padded 176 - relaxed="very" 177 - columns="equal" 178 - className={styles.contentGrid} 179 - > 180 - <Grid.Column stretched verticalAlign="middle"> 341 + <Grid> 342 + <Grid.Column width={8}> 181 343 <Segment basic> 182 - <Header as="h1">Time Interval</Header> 344 + <Header as="h1">Inter-trial interval</Header> 183 345 <p> 184 346 Select the inter-trial interval duration. This is the amount 185 347 of time between trials measured from the end of one trial to ··· 190 352 <ParamSlider 191 353 label="ITI Duration (seconds)" 192 354 value={this.state.params.iti} 355 + marks={{ 356 + 1: '0.25', 357 + 2: '0.5', 358 + 3: '0.75', 359 + 4: '1', 360 + 5: '1.25', 361 + 6: '1.5', 362 + 7: '1.75', 363 + 8: '2' 364 + }} 365 + ms_conversion='250' 193 366 onChange={value => 194 367 this.setState({ 195 - params: { ...this.state.params, iti: value } 368 + params: { ...this.state.params, iti: value }, 369 + saved: false, 370 + }) 371 + } 372 + /> 373 + </Segment> 374 + </Grid.Column> 375 + 376 + <Grid.Column width={8}> 377 + <Segment basic> 378 + <Header as="h1">Image duration</Header> 379 + <p> 380 + Select the time of presentaiton or make it self-paced. 381 + </p> 382 + </Segment> 383 + <Segment basic> 384 + <Checkbox 385 + defaultChecked={this.state.params.selfPaced} 386 + label="Self-paced data collection" 387 + onChange={value => 388 + this.setState({ 389 + params: { ...this.state.params, selfPaced: !this.state.params.selfPaced }, 390 + saved: false, 196 391 }) 197 392 } 198 393 /> 199 394 </Segment> 395 + <Segment basic> 396 + 397 + {!this.state.params.selfPaced && <ParamSlider 398 + label="Presentation time (seconds)" 399 + value={this.state.params.presentationTime} 400 + marks={{ 401 + 1: '0.25', 402 + 2: '0.5', 403 + 3: '0.75', 404 + 4: '1', 405 + 5: '1.25', 406 + 6: '1.5', 407 + 7: '1.75', 408 + 8: '2' 409 + }} 410 + ms_conversion='250' 411 + onChange={value => 412 + this.setState({ 413 + params: { ...this.state.params, presentationTime: value }, 414 + saved: false, 415 + }) 416 + } 417 + />} 418 + </Segment> 200 419 </Grid.Column> 420 + 201 421 </Grid> 202 422 ); 203 423 case CUSTOM_STEPS.PREVIEW: ··· 230 450 placeholder="You will view a series of images..." 231 451 onChange={(event, data) => 232 452 this.setState({ 233 - params: { ...this.state.params, intro: data.value } 453 + params: { ...this.state.params, intro: data.value }, 454 + saved: false, 455 + }) 456 + } 457 + /> 458 + </Form> 459 + </Segment> 460 + 461 + <Segment basic> 462 + <Form> 463 + <Form.TextArea 464 + autoHeight 465 + style={{ minHeight: 50 }} 466 + label={FIELDS.HELP} 467 + value={this.state.params.taskHelp} 468 + placeholder="Press 1 for ..." 469 + onChange={(event, data) => 470 + this.setState({ 471 + params: { ...this.state.params, taskHelp: data.value }, 472 + saved: false, 234 473 }) 235 474 } 236 475 /> 237 476 </Form> 238 477 </Segment> 478 + 239 479 <PreviewButton 240 480 isPreviewing={this.state.isPreviewing} 241 481 onClick={e => this.handlePreview(e)} ··· 274 514 description: { 275 515 ...this.state.description, 276 516 question: data.value 277 - } 517 + }, 518 + saved: false, 278 519 }) 279 520 } 280 521 /> ··· 300 541 description: { 301 542 ...this.state.description, 302 543 hypothesis: data.value 303 - } 544 + }, 545 + saved: false, 304 546 }) 305 547 } 306 548 /> ··· 326 568 description: { 327 569 ...this.state.description, 328 570 methods: data.value 329 - } 571 + }, 572 + saved: false, 330 573 }) 331 574 } 332 575 /> ··· 347 590 onStepClick={this.handleStepClick} 348 591 enableEEGToggle={ 349 592 <Checkbox 593 + toggle 350 594 defaultChecked={this.props.isEEGEnabled} 351 - label="Enable EEG" 352 595 onChange={(event, data) => this.handleEEGEnabled(event, data)} 596 + className={styles.EEGToggle} 353 597 /> 354 598 } 355 - button={ 356 - <Button 357 - compact 358 - size="small" 359 - primary 360 - onClick={() => { 361 - this.handleSaveParams(); 362 - this.handleStartExperiment(); 363 - }} 364 - > 365 - Collect Data 366 - </Button> 367 - } 368 599 saveButton={ 369 600 <Button 370 601 compact ··· 372 603 secondary 373 604 onClick={() => { 374 605 this.handleSaveParams(); 375 - this.props.experimentActions.saveWorkspace() 376 606 }} 377 607 > 378 - <Icon name="save" /> 379 - Save 608 + {this.state.saved ? 'Save' : 'Save'} 380 609 </Button> 381 610 } 382 611 /> ··· 385 614 ); 386 615 } 387 616 } 617 + 618 + // saveButton={ 619 + // <Button 620 + // compact 621 + // size="small" 622 + // secondary 623 + // onClick={() => { 624 + // this.handleSaveParams(); 625 + // // this.props.experimentActions.saveWorkspace() 626 + // }} 627 + // > 628 + // {this.state.saved ? 'Saved' : 'Save'} 629 + // </Button> 630 + // } 631 + 632 + // 633 + // button={ 634 + // <Button 635 + // compact 636 + // size="small" 637 + // primary 638 + // onClick={() => { 639 + // this.handleSaveParams(); 640 + // this.handleStartExperiment(); 641 + // }} 642 + // > 643 + // Start 644 + // </Button> 645 + // } 646 + // 647 + // <Button 648 + // compact 649 + // size="small" 650 + // secondary 651 + // onClick={() => { 652 + // this.handleSaveParams(); 653 + // }} 654 + // > 655 + // {this.state.saved ? 'Saved' : 'Save'} 656 + // </Button> 657 + // enableEEGToggle={ 658 + // <Checkbox 659 + // defaultChecked={this.props.isEEGEnabled} 660 + // label="Enable EEG" 661 + // onChange={(event, data) => this.handleEEGEnabled(event, data)} 662 + // /> 663 + // } 664 + // 665 + // <Segment basic style={{ overflow: 'auto', maxHeight: 400 }}> 666 + // <Form> 667 + // {this.state.params.stimuli && this.state.params.stimuli.map((e,num) => ( 668 + // <StimuliRow 669 + // key={num} 670 + // num={num} 671 + // conditions={[1,2,3,4].map(n => this.state.params[`stimulus${n}`].title)} 672 + // {...e} 673 + // onDelete={(num) => { 674 + // const stimuli = this.state.params.stimuli; 675 + // stimuli.splice(num, 1); 676 + // const nbPracticeTrials = stimuli.filter(s => (s.phase === 'practice')).length; 677 + // const nbTrials = stimuli.filter(s => (s.phase === 'main')).length; 678 + // this.setState({ 679 + // params: { 680 + // ...this.state.params, 681 + // stimuli: [ ... stimuli ], 682 + // nbPracticeTrials, 683 + // nbTrials, 684 + // }, 685 + // saved: false, 686 + // }) 687 + // }} 688 + // onChange={(num, key, data) => { 689 + // const stimuli = this.state.params.stimuli; 690 + // stimuli[num][key] = data; 691 + // let nbPracticeTrials = this.state.params.nbPracticeTrials; 692 + // let nbTrials = this.state.params.nbTrials; 693 + // if(key === 'phase'){ 694 + // nbPracticeTrials = stimuli.filter(s => (s.phase === 'practice')).length; 695 + // nbTrials = stimuli.filter(s => (s.phase === 'main')).length; 696 + // } 697 + // this.setState({ 698 + // params: { 699 + // ...this.state.params, 700 + // stimuli: [ ... stimuli ], 701 + // nbPracticeTrials, 702 + // nbTrials, 703 + // }, 704 + // saved: false, 705 + // }) 706 + // }} 707 + // /> 708 + // ))} 709 + // </Form> 710 + // </Segment>
+18 -23
app/components/DesignComponent/ParamSlider.js
··· 1 + 1 2 import { Segment } from 'semantic-ui-react'; 2 3 import React, { PureComponent } from 'react'; 3 4 import Slider from 'rc-slider'; ··· 9 10 onChange: number => void; 10 11 } 11 12 12 - const marks = { 13 - 1: '0.25', 14 - 2: '0.5', 15 - 3: '0.75', 16 - 4: '1', 17 - 5: '1.25', 18 - 6: '1.5', 19 - 7: '1.75', 20 - 8: '2' 21 - }; 22 - 23 - // Converts from a 1-8 scale to a range from 250 to 2000 ms 24 - const MS_CONVERSION = 250; 25 - 26 13 export default class ParamSlider extends PureComponent<Props> { 27 14 render() { 15 + 16 + const { marks, ms_conversion } = this.props; 28 17 return ( 29 18 <div> 30 19 <p className={styles.label}>{this.props.label}</p> 31 20 <Segment basic> 32 - <Slider 33 - dots 34 - marks={marks} 35 - min={1} 36 - max={8} 37 - value={this.props.value / MS_CONVERSION} 38 - onChange={value => this.props.onChange(value * MS_CONVERSION)} 39 - defaultValue={1} 40 - /> 21 + { this.props.label !== 'Practice trials' || Object.keys(marks).length > 1 ? 22 + <Slider 23 + dots 24 + marks={this.props.marks} 25 + min={Math.min(...Object.keys(marks))} 26 + max={Math.max(...Object.keys(marks))} 27 + value={this.props.value / parseInt(ms_conversion)} 28 + onChange={value => this.props.onChange(value * parseInt(ms_conversion))} 29 + defaultValue={1} 30 + /> 31 + : 32 + <div> 33 + You have not chosen any practice trials. 34 + </div> 35 + } 41 36 </Segment> 42 37 </div> 43 38 );
+71 -58
app/components/DesignComponent/StimuliDesignColumn.js
··· 1 1 /* Breaking this component on its own is done mainly to increase performance. Text input is slow otherwise */ 2 2 3 3 import React, { Component } from 'react'; 4 - import { Grid, Segment, Header, Form } from 'semantic-ui-react'; 4 + import { Form, Button, Table, Dropdown, Segment, Icon } from 'semantic-ui-react'; 5 5 import { toast } from 'react-toastify'; 6 6 import { readImages } from '../../utils/filesystem/storage'; 7 7 import { loadFromSystemDialog } from '../../utils/filesystem/select'; 8 8 import { FILE_TYPES } from '../../constants/constants'; 9 + import * as path from 'path'; 10 + import styles from "../styles/common.css"; 9 11 10 12 interface Props { 11 13 num: number; ··· 25 27 constructor(props: Props) { 26 28 super(props); 27 29 this.handleSelectFolder = this.handleSelectFolder.bind(this); 30 + this.handleRemoveFolder = this.handleRemoveFolder.bind(this); 31 + this.state = { 32 + numberImages: undefined 33 + } 28 34 } 29 35 30 36 shouldComponentUpdate(nextProps) { ··· 40 46 41 47 async handleSelectFolder() { 42 48 const dir = await loadFromSystemDialog(FILE_TYPES.STIMULUS_DIR); 43 - const images = readImages(dir); 44 - 45 - if (images.length < 1) { 46 - toast.error('No images in folder!'); 49 + if(dir){ 50 + const images = readImages(dir); 51 + if (images.length < 1) { 52 + toast.error('No images in folder!'); 53 + } 54 + this.setState({ 55 + numberImages: images.length 56 + }); 57 + this.props.onChange('dir', dir, `stimulus${this.props.num}`); 47 58 } 48 - this.props.onChange('dir', dir); 59 + } 60 + 61 + handleRemoveFolder(){ 62 + this.setState({ 63 + numberImages: 0 64 + }); 65 + this.props.onChange('dir', '', `stimulus${this.props.num}`); 49 66 } 50 67 51 68 render() { 52 69 return ( 53 - <Grid.Column stretched verticalAlign="middle"> 54 - <Segment basic> 55 - <Header as="h1">Condition {this.props.num}</Header> 56 - <p> 57 - Give your condition a title, select the location of your images, and 58 - choose the correct key response 59 - </p> 60 - </Segment> 61 - <Segment basic> 70 + <Table.Row 71 + className={styles.conditionRow} 72 + > 73 + 74 + <Table.Cell className={styles.conditionsNameRow}> 75 + { this.props.num } 62 76 <Form> 63 - <Form.Group> 64 - <Form.Input 65 - width={10} 66 - label="Title" 67 - value={this.props.title} 68 - onChange={(event, data) => 69 - this.props.onChange('title', data.value) 70 - } 71 - placeholder="e.g. Faces" 72 - /> 73 - <Form.Dropdown 74 - selection 75 - width={4} 76 - label="Correct Response" 77 - value={this.props.response} 78 - onChange={(event, data) => 79 - this.props.onChange('response', data.value) 80 - } 81 - placeholder="Response" 82 - options={RESPONSE_OPTIONS} 83 - /> 84 - </Form.Group> 85 - <Grid> 86 - <Grid.Column width={6}> 87 - <Form.Button 88 - secondary 89 - label="Location" 90 - onClick={this.handleSelectFolder} 91 - > 92 - Select Folder 93 - </Form.Button> 94 - </Grid.Column> 95 - <Grid.Column verticalAlign="bottom" floated="left" width={4}> 96 - <Segment basic compact> 97 - <em> 98 - {// Kind of hacky. Set to empty string if dir is inside app itself (default images) 99 - this.props.dir.includes('app') ? '' : this.props.dir} 100 - </em> 101 - </Segment> 102 - </Grid.Column> 103 - </Grid> 77 + <Form.Input 78 + value={this.props.title} 79 + onChange={(event, data) => 80 + this.props.onChange('title', data.value, `stimulus${this.props.num}`) 81 + } 82 + placeholder="Enter condition name" 83 + /> 104 84 </Form> 105 - </Segment> 106 - </Grid.Column> 85 + </Table.Cell> 86 + 87 + <Table.Cell className={styles.experimentRowName}> 88 + <Form.Select 89 + fluid 90 + selection 91 + value={this.props.response} 92 + onChange={(event, data) => 93 + this.props.onChange('response', data.value, `stimulus${this.props.num}`) 94 + } 95 + placeholder="Select" 96 + options={RESPONSE_OPTIONS} 97 + /> 98 + </Table.Cell> 99 + 100 + <Table.Cell className={styles.experimentRowName}> 101 + { this.props.dir ? 102 + <div className={styles.selectedFolderContainer}> 103 + <div>Folder {this.props.dir && this.props.dir.split(path.sep).slice(-1).join(' / ')}</div> 104 + <div>( {this.state.numberImages || this.props.numberImages} items ) </div> 105 + <div><Icon name="delete" onClick={this.handleRemoveFolder} /></div> 106 + </div> 107 + : 108 + <Button 109 + secondary 110 + onClick={this.handleSelectFolder} 111 + > 112 + Select folder 113 + </Button> 114 + } 115 + 116 + </Table.Cell> 117 + 118 + </Table.Row> 119 + 107 120 ); 108 121 } 109 122 }
+84
app/components/DesignComponent/StimuliRow.js
··· 1 + /* Breaking this component on its own is done mainly to increase performance. Text input is slow otherwise */ 2 + 3 + import React, { Component } from 'react'; 4 + import { Grid, Segment, Header, Form, Label, Icon, Button, Table } from 'semantic-ui-react'; 5 + import { toast } from 'react-toastify'; 6 + import styles from '../styles/common.css'; 7 + 8 + interface Props { 9 + num: number; 10 + response: string; 11 + dir: string; 12 + condition: string; 13 + onChange: (string, string) => void; 14 + } 15 + 16 + const RESPONSE_OPTIONS = new Array(10).fill(0).map((_, i) => ({ 17 + key: i.toString(), 18 + text: i.toString(), 19 + value: i.toString() 20 + })); 21 + 22 + export default class StimuliRow extends Component<Props> { 23 + constructor(props: Props) { 24 + super(props); 25 + } 26 + 27 + render() { 28 + return ( 29 + <Table.Row className={styles.trialsRow}> 30 + <Table.Cell className={styles.conditionsNameRow}> 31 + <div style={{'alignSelf':'center'}}> 32 + {this.props.num + 1}. 33 + </div> 34 + <div> 35 + {this.props.name} 36 + </div> 37 + </Table.Cell> 38 + 39 + <Table.Cell className={styles.experimentRowName}> 40 + <div> 41 + {this.props.condition} 42 + </div> 43 + </Table.Cell> 44 + 45 + <Table.Cell className={styles.experimentRowName}> 46 + <Form.Select 47 + fluid 48 + selection 49 + value={this.props.response} 50 + onChange={(event, data) => 51 + this.props.onChange(this.props.num, 'response', data.value) 52 + } 53 + placeholder="Response" 54 + options={RESPONSE_OPTIONS} 55 + /> 56 + </Table.Cell> 57 + 58 + <Table.Cell className={styles.trialsTrialTypeRow}> 59 + <Form.Select 60 + fluid 61 + selection 62 + value={this.props.phase} 63 + onChange={(event, data) => 64 + this.props.onChange(this.props.num, 'phase', data.value) 65 + } 66 + placeholder="Response" 67 + options={[{key: 'main', text: 'Experimental', value: 'main'}, 68 + {key: 'practice', text: 'Practice', value: 'practice'}]} 69 + /> 70 + <Button 71 + secondary 72 + onClick={() => { 73 + this.props.onDelete(this.props.num) 74 + }} 75 + > 76 + Delete 77 + </Button> 78 + </Table.Cell> 79 + 80 + </Table.Row> 81 + 82 + ); 83 + } 84 + }
+23 -14
app/components/DesignComponent/index.js
··· 44 44 import { toast } from 'react-toastify'; 45 45 import InputModal from '../InputModal'; 46 46 47 + import { shell } from 'electron'; 48 + 47 49 const DESIGN_STEPS = { 48 50 OVERVIEW: 'OVERVIEW', 49 51 BACKGROUND: 'BACKGROUND', ··· 246 248 className={styles.contentGrid} 247 249 style={{ alignItems: 'center' }} 248 250 > 249 - <Grid.Row stretched> 251 + <Grid.Row> 250 252 <Grid.Column stretched width={4}> 251 253 <Segment basic> 252 254 <Image src={this.renderOverviewIcon(this.props.type)} /> 253 255 </Segment> 254 256 </Grid.Column> 255 257 256 - <Grid.Column stretched width={6}> 258 + <Grid.Column stretched width={5}> 257 259 <Segment basic> 258 260 <p>{this.props.background_first_column}</p> 259 261 <p style={{ fontWeight: 'bold' }}> ··· 262 264 </Segment> 263 265 </Grid.Column> 264 266 265 - <Grid.Column stretched width={6}> 267 + <Grid.Column stretched width={5}> 266 268 <Segment basic> 267 269 <p>{this.props.background_second_column}</p> 268 270 <p style={{ fontWeight: 'bold' }}> ··· 270 272 </p> 271 273 </Segment> 272 274 </Grid.Column> 275 + 276 + <Grid.Column width={2}> 277 + <Segment basic> 278 + <div className={styles.externalLinks}> 279 + {this.props.background_links.map(link => ( 280 + <Button key={link.address} secondary onClick={()=>{shell.openExternal(link.address)}}> 281 + {link.name} 282 + </Button> 283 + ))} 284 + </div> 285 + </Segment> 286 + </Grid.Column> 287 + 273 288 </Grid.Row> 289 + 274 290 </Grid> 275 291 ); 276 292 ··· 373 389 steps={DESIGN_STEPS} 374 390 activeStep={this.state.activeStep} 375 391 onStepClick={this.handleStepClick} 392 + onEditClick={this.handleCustomizeExperiment} 376 393 enableEEGToggle={ 377 394 <Checkbox 395 + toggle 378 396 defaultChecked={this.props.isEEGEnabled} 379 - label="Enable EEG" 380 397 onChange={(event, data) => this.handleEEGEnabled(event, data)} 398 + className={styles.EEGToggle} 381 399 /> 382 400 } 383 - customizeButton={ 384 - <Button secondary onClick={this.handleCustomizeExperiment}> 385 - Edit 386 - </Button> 387 - } 388 - button={ 389 - <Button primary onClick={this.handleStartExperiment}> 390 - Collect Data 391 - </Button> 392 - } 401 + canEditExperiment={this.props.paradigm === 'Faces and Houses'} 393 402 /> 394 403 {this.renderSectionContent()} 395 404 <InputModal
+89 -41
app/components/HomeComponent/index.js
··· 1 1 // @flow 2 2 import React, { Component } from "react"; 3 3 import { isNil } from "lodash"; 4 - import { Grid, Button, Header, Segment, Image } from "semantic-ui-react"; 4 + import { Grid, Button, Header, Segment, Image, Table } from "semantic-ui-react"; 5 5 import { toast } from "react-toastify"; 6 + import * as moment from 'moment'; 7 + 6 8 import styles from "../styles/common.css"; 7 - import { EXPERIMENTS, SCREENS, KERNEL_STATUS } from "../../constants/constants"; 9 + import { EXPERIMENTS, SCREENS, KERNEL_STATUS, PLOTTING_INTERVAL, CONNECTION_STATUS, DEVICE_AVAILABILITY } from "../../constants/constants"; 8 10 import faceHouseIcon from "../../assets/common/FacesHouses.png"; 9 11 import stroopIcon from "../../assets/common/Stroop.png"; 10 12 import multitaskingIcon from "../../assets/common/Multitasking.png"; 11 13 import searchIcon from "../../assets/common/VisualSearch.png"; 12 14 import customIcon from "../../assets/common/Custom.png"; 13 15 import appLogo from "../../assets/common/app_logo.png"; 16 + import divingMan from "../../assets/common/divingMan.svg"; 14 17 import { 15 18 readWorkspaces, 16 19 readAndParseState, 17 - openWorkspaceDir 20 + openWorkspaceDir, 21 + deleteWorkspaceDir 18 22 } from "../../utils/filesystem/storage"; 19 23 import { 20 24 Collect, ··· 27 31 import { loadProtocol } from "../../utils/labjs/functions"; 28 32 import SignalQualityIndicatorComponent from "../SignalQualityIndicatorComponent"; 29 33 import ViewerComponent from "../ViewerComponent"; 30 - import { 31 - PLOTTING_INTERVAL, 32 - CONNECTION_STATUS, 33 - DEVICE_AVAILABILITY 34 - } from "../../constants/constants"; 35 34 import ConnectModal from "../CollectComponent/ConnectModal"; 36 35 36 + import { remote } from 'electron'; 37 + 38 + const { dialog } = remote; 39 + 40 + 37 41 const HOME_STEPS = { 38 42 // TODO: maybe change the recent and new labels, but not necessary right now 39 43 RECENT: "MY EXPERIMENTS", ··· 74 78 handleCloseOverview: () => void; 75 79 handleConnectModalClose: () => void; 76 80 handleStartConnect: () => void; 81 + handleDeleteWorkspace: string => void; 77 82 78 83 constructor(props: Props) { 79 84 super(props); ··· 95 100 this.handleConnectModalClose = this.handleConnectModalClose.bind(this); 96 101 this.handleStartConnect = this.handleStartConnect.bind(this); 97 102 this.handleStopConnect = this.handleStopConnect.bind(this); 103 + this.handleDeleteWorkspace = this.handleDeleteWorkspace.bind(this); 98 104 } 99 105 100 106 componentDidMount() { ··· 189 195 this.setState({ isConnectModalOpen: false }); 190 196 } 191 197 198 + handleDeleteWorkspace(dir){ 199 + const options = { 200 + buttons: ["No", "Yes"], 201 + message: "Do you really want to delete the experiment?" 202 + } 203 + const response = dialog.showMessageBox(options) 204 + if(response === 1){ 205 + deleteWorkspaceDir(dir); 206 + this.setState({ recentWorkspaces: readWorkspaces() }); 207 + } 208 + } 209 + 192 210 // TODO: Figure out how to make this not overflow when there's tons of workspaces. Lists? 193 211 renderSectionContent() { 194 212 switch (this.state.activeStep) { 195 213 case HOME_STEPS.RECENT: 196 214 return ( 197 - <Grid stackable padded columns="equal"> 198 - {this.state.recentWorkspaces.length > 0 ? this.state.recentWorkspaces.map(dir => ( 199 - <Grid.Row key={dir}> 215 + <Grid stackable padded columns="equal" className={styles.myExperimentsPage}> 216 + {this.state.recentWorkspaces.length > 0 ? 217 + 218 + <Table basic="very"> 219 + <Table.Header> 220 + <Table.Row className={styles.experimentHeaderRow}> 221 + <Table.HeaderCell className={styles.experimentHeaderName}>Experiment name</Table.HeaderCell> 222 + <Table.HeaderCell>Date Modified</Table.HeaderCell> 223 + <Table.HeaderCell className={styles.experimentHeaderActionsName}>Actions</Table.HeaderCell> 224 + </Table.Row> 225 + </Table.Header> 226 + <Table.Body className={styles.experimentTable}> 227 + {this.state.recentWorkspaces.map(dir => { 228 + const {params: {dateModified}} = readAndParseState(dir); 229 + return ( 230 + <Table.Row key={dir} className={styles.experimentRow}> 231 + <Table.Cell className={styles.experimentRowName}> 232 + {dir} 233 + </Table.Cell> 234 + <Table.Cell className={styles.experimentRowName}> 235 + {dateModified && moment.default(dateModified).fromNow()} 236 + </Table.Cell> 237 + <Table.Cell className={styles.experimentRowName}> 238 + <Button 239 + secondary 240 + onClick={() => this.handleDeleteWorkspace(dir)} 241 + className={styles.experimentBtn} 242 + > 243 + Delete 244 + </Button> 245 + <Button 246 + secondary 247 + onClick={() => openWorkspaceDir(dir)} 248 + className={styles.experimentBtn} 249 + > 250 + Go to Folder 251 + </Button> 252 + <Button 253 + primary 254 + onClick={() => this.handleLoadRecentWorkspace(dir)} 255 + className={styles.experimentBtn} 256 + > 257 + Open Experiment 258 + </Button> 259 + </Table.Cell> 260 + </Table.Row> 261 + ) 262 + })} 263 + </Table.Body> 264 + </Table> 265 + : 266 + <Grid.Column textAlign="center"> 267 + <Image src={divingMan} centered className={styles.noExperimentsImage} /> 268 + <Header className={styles.noExperimentsTitle}> 269 + You don&apos;t have any experiments yet 270 + </Header> 271 + <p className={styles.noExperimentsText}> 272 + Head over to the &quot;Experiment Bank&quot; section to start an experiment. 273 + </p> 200 274 <Button 201 - secondary 202 - onClick={() => this.handleLoadRecentWorkspace(dir)} 275 + primary 276 + onClick={() => this.handleStepClick('EXPERIMENT BANK')} 203 277 > 204 - Open Experiment 278 + View Experiments 205 279 </Button> 206 - <Segment className={styles.recentDirSegment} vertical basic> 207 - <Header as="h3">{dir}</Header> 208 - </Segment> 209 - <Button 210 - icon="folder open outline" 211 - basic 212 - circular 213 - size="huge" 214 - className={styles.closeButton} 215 - onClick={() => openWorkspaceDir(dir)} 216 - /> 217 - </Grid.Row> 218 - )) : 219 - <Grid.Column> 220 - <Segment className={styles.recentDirSegment} vertical basic> 221 - <Header as="h3">You don't have any experiments</Header> 222 - </Segment> 223 - <p> 224 - Head over to the "Templates" section to start an experiment. 225 - </p> 226 - <Button 227 - secondary 228 - onClick={() => this.handleStepClick('EXPERIMENT BANK')} 229 - > 230 - View Templates 231 - </Button> 232 - </Grid.Column> 280 + </Grid.Column> 233 281 } 234 282 </Grid> 235 283 );
+5
app/components/PreviewExperimentComponent.js
··· 34 34 return getImages(this.props.params); 35 35 } 36 36 37 + insertPreviewLabJsCallback(e) { 38 + console.log('EEG marker', e); 39 + } 40 + 37 41 render() { 38 42 if (!this.props.isPreviewing) { 39 43 return <Segment basic />; ··· 44 48 settings={{ 45 49 script: this.props.paradigm, 46 50 params: this.props.previewParams || this.props.params, 51 + eventCallback: this.insertPreviewLabJsCallback, 47 52 on_finish: csv => { 48 53 this.props.onEnd(); 49 54 }
+1 -1
app/components/SecondaryNavComponent/SecondaryNavSegment.js
··· 16 16 <Grid.Column 17 17 as="a" 18 18 onClick={this.props.onClick} 19 - width={2} 20 19 textAlign="center" 20 + verticalAlign="bottom" 21 21 className={[this.props.style, styles.secondaryNavSegment].join(' ')} 22 22 > 23 23 {this.props.title}
+27 -14
app/components/SecondaryNavComponent/index.js
··· 1 1 import React, { Component } from 'react'; 2 - import { Grid, Header, Checkbox, Segment } from 'semantic-ui-react'; 2 + import { Grid, Header, Dropdown } from 'semantic-ui-react'; 3 3 import styles from '../styles/secondarynav.css'; 4 4 import SecondaryNavSegment from './SecondaryNavSegment'; 5 5 ··· 18 18 19 19 renderTitle() { 20 20 if (typeof this.props.title === 'string') { 21 - return <Header as="h2">{this.props.title}</Header>; 21 + return <Header className={styles.secondaryNavContainerExpName}>{this.props.title}</Header>; 22 22 } 23 23 return this.props.title; 24 24 } ··· 38 38 onClick={() => this.props.onStepClick(stepTitle)} 39 39 /> 40 40 ))} 41 - {this.props.enableEEGToggle ? ( 42 - <Segment basic> 43 - {this.props.enableEEGToggle} 44 - </Segment> 45 - ) : null} 46 - {this.props.button ? ( 47 - <Grid.Column width="3" floated="right" textAlign="right"> 48 - {this.props.customizeButton} 49 - {this.props.saveButton} 50 - {this.props.button} 51 - </Grid.Column> 52 - ) : null} 53 41 </React.Fragment> 54 42 ); 55 43 } 56 44 45 + 46 + 57 47 render() { 48 + 58 49 return ( 59 50 <Grid verticalAlign="middle" className={styles.secondaryNavContainer}> 60 51 <Grid.Column width={3}>{this.renderTitle()}</Grid.Column> 61 52 {this.renderSteps()} 53 + 54 + {this.props.enableEEGToggle && 55 + <Grid.Column width={2} floated="right"> 56 + <div className={styles.settingsButtons}> 57 + <Dropdown icon='setting' direction="left" fluid className={styles.dropdownSettings}> 58 + <Dropdown.Menu className={styles.dropdownMenu}> 59 + <Dropdown.Item className={styles.dropdownItem}> 60 + <div>Enable EEG</div> 61 + {this.props.enableEEGToggle} 62 + </Dropdown.Item> 63 + {this.props.canEditExperiment && 64 + <Dropdown.Item 65 + text='Edit Experiment' 66 + onClick={() => this.props.onEditClick()} 67 + /> 68 + } 69 + </Dropdown.Menu> 70 + </Dropdown> 71 + {this.props.saveButton} 72 + </div> 73 + </Grid.Column> 74 + } 62 75 </Grid> 63 76 ); 64 77 }
+10 -10
app/components/TopNavComponent/index.js
··· 44 44 return ( 45 45 <Grid 46 46 className={styles.navContainer} 47 + verticalAlign="middle" 47 48 columns="equal" 48 - textAlign="center" 49 - verticalAlign="middle" 50 49 > 51 - <Grid.Column width="3" className={styles.experimentTitleSegment}> 50 + <Grid.Column className={styles.experimentTitleSegment}> 52 51 <Segment basic as="p"> 52 + <NavLink to={SCREENS.HOME.route}> 53 + <span className={styles.exitWorkspaceBtn}> 54 + B 55 + </span> 56 + </NavLink> 53 57 {this.props.title ? this.props.title : 'Untitled'} 54 58 </Segment> 55 59 </Grid.Column> ··· 78 82 style={this.getStyleForScreen(SCREENS.ANALYZE)} 79 83 /> 80 84 )} 81 - <Grid.Column width="3"> 82 - <NavLink to={SCREENS.HOME.route}> 83 - <Button secondary size="medium"> 84 - Exit Workspace 85 - </Button> 86 - </NavLink> 87 - </Grid.Column> 85 + 86 + 87 + 88 88 </Grid> 89 89 ); 90 90 }
+171
app/components/styles/common.css
··· 81 81 height: inherit; 82 82 align-items: center; 83 83 margin-bottom: 20px; 84 + border: solid 1px #007c70; 85 + border-radius: 4px; 84 86 } 85 87 86 88 .experimentWindow { ··· 125 127 height: 80%; 126 128 } 127 129 130 + .externalLinks { 131 + display: grid; 132 + grid-template-columns: 1fr; 133 + grid-row-gap: 10px; 134 + 135 + 136 + } 137 + 128 138 .experimentCard { 129 139 border-radius: 5px; 130 140 background-color: #FFFFFF; ··· 158 168 letter-spacing: 0.57px; 159 169 line-height: 24px; 160 170 } 171 + 172 + .noExperimentsImage { 173 + margin-top: 50px; 174 + } 175 + 176 + .noExperimentsTitle { 177 + font-family: Lato; 178 + font-style: normal; 179 + font-weight: normal !important; 180 + font-size: 24px !important; 181 + line-height: 29px; 182 + letter-spacing: -0.2px; 183 + color: #1A1A1A; 184 + } 185 + 186 + .noExperimentsText{ 187 + font-family: Lato; 188 + font-style: normal; 189 + font-weight: normal; 190 + font-size: 18px; 191 + line-height: 24px; 192 + letter-spacing: -0.2px; 193 + color: #1A1A1A; 194 + } 195 + 196 + .myExperimentsPage { 197 + padding-top: 50px !important; 198 + } 199 + 200 + .experimentRow { 201 + background-color: #FFFFFF !important; 202 + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1); 203 + border-radius: 4px; 204 + display: grid; 205 + grid-template-columns: 1fr 1fr auto; 206 + align-items: center; 207 + justify-content: left; 208 + } 209 + 210 + .experimentRowName { 211 + font-family: Lato; 212 + font-style: normal !important; 213 + font-weight: normal !important; 214 + font-size: 18px !important; 215 + line-height: 24px !important; 216 + letter-spacing: -0.2px; 217 + color: #1A1A1A; 218 + padding-left: 26px !important; 219 + border: 0px !important; 220 + padding-right: 10px !important; 221 + } 222 + 223 + .experimentHeaderName { 224 + padding-left: 26px !important; 225 + } 226 + 227 + .experimentHeaderRow { 228 + display: grid; 229 + grid-template-columns: 1fr 1fr auto; 230 + } 231 + 232 + .experimentTable { 233 + display: grid; 234 + grid-row-gap: 10px; 235 + align-items: center; 236 + grid-template-columns: 1fr; 237 + } 238 + 239 + .experimentHeaderActionsName{ 240 + min-width: 495px !important; 241 + } 242 + 243 + .conditionsNameRow { 244 + font-family: Lato; 245 + font-style: normal !important; 246 + font-weight: normal !important; 247 + font-size: 18px !important; 248 + line-height: 24px !important; 249 + letter-spacing: -0.2px; 250 + color: #1A1A1A; 251 + padding-left: 26px !important; 252 + border: 0px !important; 253 + padding-right: 10px !important; 254 + display: grid; 255 + grid-template-columns: 50px 1fr; 256 + align-items: center; 257 + } 258 + 259 + .conditionHeaderRowName { 260 + padding-left: 60px !important; 261 + } 262 + 263 + .conditionRow { 264 + background-color: #FFFFFF !important; 265 + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1); 266 + border-radius: 4px; 267 + display: grid; 268 + grid-template-columns: 300px 200px 1fr; 269 + align-items: center; 270 + justify-content: left; 271 + } 272 + 273 + .conditionHeaderRow { 274 + display: grid; 275 + grid-template-columns: 300px 200px 1fr; 276 + padding-left: 20px !important; 277 + } 278 + 279 + .trialsHeaderRow { 280 + display: grid; 281 + grid-template-columns: 1fr 1fr 180px 350px; 282 + padding-left: 20px !important; 283 + } 284 + 285 + .trialsRow { 286 + background-color: #FFFFFF !important; 287 + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1); 288 + border-radius: 4px; 289 + display: grid; 290 + grid-template-columns: 1fr 1fr 180px 350px; 291 + align-items: center; 292 + justify-content: left; 293 + } 294 + 295 + .trialsTrialTypeRow { 296 + font-family: Lato; 297 + font-style: normal !important; 298 + font-weight: normal !important; 299 + font-size: 18px !important; 300 + line-height: 24px !important; 301 + letter-spacing: -0.2px; 302 + color: #1A1A1A; 303 + padding-left: 26px !important; 304 + border: 0px !important; 305 + padding-right: 10px !important; 306 + display: grid; 307 + grid-template-columns: 1fr 120px; 308 + align-items: center; 309 + grid-column-gap: 30px; 310 + } 311 + 312 + .trialsTable { 313 + display: grid; 314 + grid-row-gap: 10px; 315 + align-items: center; 316 + align-content: baseline; 317 + grid-template-columns: 1fr; 318 + overflow-y: scroll; 319 + min-height: 500px; 320 + max-height: 500px; 321 + } 322 + 323 + .EEGToggle{ 324 + transform: scale(0.7); 325 + } 326 + 327 + .selectedFolderContainer{ 328 + display: grid; 329 + grid-template-columns: auto auto 1fr; 330 + grid-column-gap: 10px; 331 + }
+34 -1
app/components/styles/secondarynav.css
··· 6 6 7 7 .secondaryNavSegment { 8 8 display: flex !important; 9 - justify-content: center; 9 + justify-content: center !important; 10 10 border-style: solid; 11 11 border-width: 0px; 12 12 border-bottom-width: 4px; ··· 14 14 font-weight: bold; 15 15 line-height: 17px; 16 16 letter-spacing: 0.5px; 17 + min-width: fit-content; 17 18 } 18 19 19 20 .inactiveSecondaryNavSegment { ··· 34 35 35 36 .activeSecondaryNavSegment:hover { 36 37 } 38 + 39 + .secondaryNavContainerExpName{ 40 + font-family: Lato; 41 + font-style: normal; 42 + font-weight: normal !important; 43 + font-size: 24px !important; 44 + line-height: 29px !important; 45 + letter-spacing: -0.2px !important; 46 + color: #1A1A1A !important; 47 + } 48 + 49 + .settingsButtons { 50 + display: grid; 51 + grid-template-columns: 2fr 4fr; 52 + align-items: center; 53 + } 54 + 55 + .dropdownSettings{ 56 + font-size: 27px; 57 + color: #666666; 58 + } 59 + 60 + .dropdownMenu { 61 + margin-left: 30px !important; 62 + } 63 + 64 + .dropdownItem { 65 + display: grid !important; 66 + grid-template-columns: 3fr 1fr !important; 67 + min-width: 240px !important; 68 + align-items: center; 69 + }
+12
app/components/styles/topnavbar.css
··· 91 91 border: none !important; 92 92 box-shadow: none !important; 93 93 } 94 + 95 + .exitWorkspaceBtn{ 96 + font-family: Gothic A1 !important; 97 + font-style: normal !important; 98 + font-weight: 900 !important; 99 + font-size: 24px !important; 100 + line-height: 30px !important; 101 + color: #007C70 !important; 102 + border: none !important; 103 + margin-left: 20px; 104 + margin-right: 40px; 105 + }
+2 -2
app/constants/constants.js
··· 59 59 60 60 // NOTE: the actual marker id values of stimulus 1 and 2 are reversed 61 61 export const EVENTS = { 62 - STIMULUS_1: 2, 63 - STIMULUS_2: 1, 62 + STIMULUS_1: 1, 63 + STIMULUS_2: 2, 64 64 TARGET: 2, 65 65 NONTARGET: 1 66 66 };
+5 -1
app/utils/filesystem/dialog.js
··· 38 38 properties: ['openDirectory'] 39 39 }, 40 40 dir => { 41 - event.sender.send('loadDialogReply', dir[0]); 41 + if(dir){ 42 + event.sender.send('loadDialogReply', dir[0]); 43 + } else { 44 + event.sender.send('loadDialogReply', ''); 45 + } 42 46 } 43 47 ); 44 48 };
+6 -2
app/utils/filesystem/storage.js
··· 132 132 const behaviorFiles = files 133 133 .filter( 134 134 filepath => 135 - filepath.slice(-12).includes('behavior.csv') || 136 - filepath.includes('aggregated') 135 + filepath.slice(-12).includes('behavior.csv') 137 136 ) 138 137 .map(filepath => ({ 139 138 name: path.basename(filepath), ··· 231 230 const csv = Papa.unparse(data); 232 231 return csv; 233 232 }; 233 + 234 + // Deletes a workspace folder 235 + export const deleteWorkspaceDir = (title: string) => { 236 + shell.moveItemToTrash(path.join(workspaces, title)); 237 + }
+9
app/utils/labjs/index.js
··· 30 30 case 'Faces and Houses': 31 31 default: 32 32 faceshouses.parameters = props.settings.params; 33 + // inject files with their addresses from parameters values 34 + faceshouses.files = props.settings.params.stimuli.map(image => { 35 + return( 36 + { [image.filename] : `${image.dir}/${image.filename}`} 37 + ) 38 + }).reduce((obj, item) => { 39 + obj[Object.keys(item)[0]] = Object.values(item)[0] 40 + return obj; 41 + }, {}); 33 42 this.study = lab.util.fromObject(clonedeep(faceshouses), lab); 34 43 break; 35 44 }
+114 -2
app/utils/labjs/protocols/faceshouses.js
··· 19 19 This has led researchers speculate that faces may be processed differently than other stimuli.`, 20 20 background_first_column_question: `In fact, there is a special area in your brain, the Fusiform Face Area, that has been shown to be selective for faces. 21 21 People who have damage in this area may have a hard time recognizing faces, a condition called face blindness, or prosopagnosia.`, 22 - background_second_column: `In this video the famous neurologist Oliver Sacks explains what it is like to have face blindness to the extent 22 + background_second_column: `In the video (Link 1) the famous neurologist Oliver Sacks explains what it is like to have face blindness to the extent 23 23 that he sometimes didn’t even recognize his own face (!)`, 24 24 background_second_column_question: `Fun fact: Brad Pitt claims he has face blindness, but he has not been tested. 25 25 Do you know anyone who has face blindness?`, ··· 31 31 protocol_condition_second_img: `conditionHouse`, 32 32 protocol_condition_second_title: `Houses`, 33 33 protocol_condition_second: `If you see a house, press “9”.`, 34 + overview_links: [], 35 + background_links: [{name: 'Link 1', address: 'https://www.cnn.com/videos/health/2011/01/04/sacks.face.blindness.cnn'}], 36 + protocal_links: [], 34 37 params: { 38 + randomize: 'random', 35 39 includePractice: true, 36 40 trialDuration: 1000, 37 - nbTrials: 150, 41 + nbTrials: 8, 42 + nbPracticeTrials: 2, 38 43 iti: 500, 44 + presentationTime: 1000, 45 + selfPaced: true, 39 46 jitter: 200, 40 47 sampleType: 'with-replacement', 41 48 pluginName: 'callback-image-display', 42 49 intro: `You will view a series of faces and houses. Press 1 when a face appears and 9 for a house. Press the the space bar on your keyboard to start doing the practice trials. If you want to skip the practice trials and go directly to the task, press the "q" button on your keyboard.`, 50 + taskHelp: `Press 1 for a face and 9 for a house`, 43 51 showProgressBar: false, 44 52 stimulus1: { 45 53 dir: facesDir, ··· 53 61 type: EVENTS.STIMULUS_2, 54 62 response: '9' 55 63 }, 64 + stimulus3: { 65 + dir: '', 66 + title: '', 67 + type: 3, 68 + response: '' 69 + }, 70 + stimulus4: { 71 + dir: '', 72 + title: '', 73 + type: 4, 74 + response: '' 75 + }, 76 + stimuli: [ 77 + { 78 + condition: "Face", 79 + dir: facesDir, 80 + filename: "Annie_3.jpg", 81 + name: "Annie_3", 82 + response: "1", 83 + phase: 'practice', 84 + type: EVENTS.STIMULUS_1, 85 + }, 86 + { 87 + condition: "Face", 88 + dir: facesDir, 89 + filename: "Blake_3.jpg", 90 + name: "Blake_3", 91 + response: "1", 92 + phase: 'main', 93 + type: EVENTS.STIMULUS_1, 94 + }, 95 + { 96 + condition: "Face", 97 + dir: facesDir, 98 + filename: "Don_3.jpg", 99 + name: "Don_3", 100 + response: "1", 101 + phase: 'main', 102 + type: EVENTS.STIMULUS_1, 103 + }, 104 + { 105 + condition: "Face", 106 + dir: facesDir, 107 + filename: "Estelle_3.jpg", 108 + name: "Estelle_3", 109 + response: "1", 110 + phase: 'main', 111 + type: EVENTS.STIMULUS_1, 112 + }, 113 + { 114 + condition: "Face", 115 + dir: facesDir, 116 + filename: "Frank_3.jpg", 117 + name: "Frank_3", 118 + response: "1", 119 + phase: 'main', 120 + type: EVENTS.STIMULUS_1, 121 + }, 122 + { 123 + condition: "House", 124 + dir: housesDir, 125 + filename: "house1.3.jpg", 126 + name: "house1.3", 127 + response: "9", 128 + phase: 'practice', 129 + type: EVENTS.STIMULUS_2, 130 + }, 131 + { 132 + condition: "House", 133 + dir: housesDir, 134 + filename: "house2.2.jpg", 135 + name: "house2.2", 136 + response: "9", 137 + phase: 'main', 138 + type: EVENTS.STIMULUS_2, 139 + }, 140 + { 141 + condition: "House", 142 + dir: housesDir, 143 + filename: "house3.1.jpg", 144 + name: "house3.1", 145 + response: "9", 146 + phase: 'main', 147 + type: EVENTS.STIMULUS_2, 148 + }, 149 + { 150 + condition: "House", 151 + dir: housesDir, 152 + filename: "house4.3.jpg", 153 + name: "house4.3", 154 + response: "9", 155 + phase: 'main', 156 + type: EVENTS.STIMULUS_2, 157 + }, 158 + { 159 + condition: "House", 160 + dir: housesDir, 161 + filename: "house5.2.jpg", 162 + name: "house5.3", 163 + response: "9", 164 + phase: 'main', 165 + type: EVENTS.STIMULUS_2, 166 + }, 167 + ] 56 168 }, 57 169 mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 58 170 trials: {
+8 -2
app/utils/labjs/protocols/multi.js
··· 17 17 Some people pride themselves with being “good multitaskers.” But is this scientifically plausible?`, 18 18 background_first_column_question: `Are some people better than others at performing multiple tasks at the same time?`, 19 19 background_second_column: `Some research suggests that good multitaskers are actually not performing different tasks simultaneously, 20 - but are instead rapidly switching back ‘n forth between different tasks (click here to read more about this research). 20 + but are instead rapidly switching back ‘n forth between different tasks (click Link 1 to read more about this research). 21 21 Other research suggests that our brains are able to distribute different tasks across hemispheres 22 - (you can read more about it here).`, 22 + (you can read more about it at Link 2).`, 23 23 background_second_column_question: `Do you think some people could be better brain ‘distributors’ than others?`, 24 24 protocol_title: `What participants are shown`, 25 25 protocol: `Participants are shown either a square or diamonds with dots inside. ··· 30 30 protocol_condition_second_img: `multiConditionDots`, 31 31 protocol_condition_second_title: `Rule 2`, 32 32 protocol_condition_second: `If the object is shown on the bottom, they need to respond to the number of dots inside (pressing ‘n’ for 3 dots and ‘b’ for 2 dots). `, 33 + overview_links: [], 34 + background_links: [ 35 + {name: 'Link 1', address: 'https://www.scientificamerican.com/podcast/episode/the-myth-of-multitasking-09-07-15/'}, 36 + {name: 'Link 2', address: 'https://www.scientificamerican.com/article/multitasking-two-tasks/'}, 37 + ], 38 + protocal_links: [], 33 39 params: { 34 40 trialDuration: 1000, 35 41 nbTrials: 150,
+3
app/utils/labjs/protocols/search.js
··· 33 33 protocol_condition_second_img: `conditionNoOrangeT`, 34 34 protocol_condition_second_title: `No orange T`, 35 35 protocol_condition_second: `If the orange T is not on the screen, press the ‘n’ key instead.`, 36 + overview_links: [], 37 + background_links: [], 38 + protocal_links: [], 36 39 params: { 37 40 trialDuration: 1000, 38 41 nbTrials: 150,
+4 -1
app/utils/labjs/protocols/stroop.js
··· 21 21 background_second_column: `Researchers have used different kinds of Stroop tasks to ask how our brains deal with contradictory information. 22 22 This may be more difficult under some conditions (for example, when we don’t get enough sleep) and for some people 23 23 (for example, children with Attention-deficit/hyperactivity disorder (ADHD)).`, 24 - background_second_column_question: `You can read more about the Stroop task here. `, 24 + background_second_column_question: `You can read more about the Stroop task at Link 1. `, 25 25 protocol_title: `What participants are shown`, 26 26 protocol: `In the Stroop task, you will see different words written in different colors 27 27 (e.g., the word “GREEN” may be written in a green-colored font, but it may also be written in a red font). ··· 33 33 protocol_condition_second_img: `conditionIncongruent`, 34 34 protocol_condition_second_title: `"Green" written in red`, 35 35 protocol_condition_second: `The color is red, so the correct response is ‘r’.`, 36 + overview_links: [], 37 + background_links: [{name: 'Link 1', address: 'https://www.psychologytoday.com/us/blog/play-in-mind/201204/when-red-looks-blue-and-yes-means-no'}], 38 + protocal_links: [], 36 39 params: { 37 40 trialDuration: 1000, 38 41 nbTrials: 150,
+184 -302
app/utils/labjs/scripts/faceshouses.js
··· 1 - import * as path from 'path'; 2 - const rootFolder = __dirname; 3 - const assetsDirectory = path.join(rootFolder, 'assets', 'labjs', 'faceshouses'); 4 - 5 1 // Define study 6 2 const studyObject = { 7 3 "title": "root", ··· 19 15 "content": [ 20 16 { 21 17 "type": "lab.flow.Sequence", 22 - "files": { 23 - "Annie_3.jpg": `${assetsDirectory}/7a536fba60226293bf351cb6f9719fee15f3b693915c0a55b0f107377f10a7e1.jpg`, 24 - "Blake_3.jpg": `${assetsDirectory}/3138f2fd1cba5a07f0b0ac61f8ee4754e6e0f07bd3ea520a75bc39c2d05ecece.jpg`, 25 - "Don_3.jpg": `${assetsDirectory}/cc6bf44e75c27e0d57956ae95d4c2c64f0d41f666a1e3f9fff20927fe3844dfd.jpg`, 26 - "Estelle_3.jpg": `${assetsDirectory}/5ce843ee780ba0c902ee1b06326c7e65fe840769c8621a8d9f8b75b5eb67bcd9.jpg`, 27 - "Frank_3.jpg": `${assetsDirectory}/856ae9bd5ec82567011ae41e10dcfa2de9cfa7ddadc5c1388df2998142ab4d7f.jpg`, 28 - "Janie_3.jpg": `${assetsDirectory}/a82a652abb85637af9fba1f4dbf7f69d906a14172a70df610e3be33c0058b9f6.jpg`, 29 - "Joan_3.jpg": `${assetsDirectory}/d92c1965ad53b7533d5f00ace843e8f1c161ae7e72ad52b4511f3ab84a8eea73.jpg`, 30 - "Jodi_3.jpg": `${assetsDirectory}/b746cdfd1099dfbe200a661a81ef2315935a5eafc855b8b373d9b633fadd8e6c.jpg`, 31 - "Joe_3.jpg": `${assetsDirectory}/3e55b3ce099ffea5a4ebde02c7a0cb055fb6f9768116efc932e77e4319841bea.jpg`, 32 - "Tim_3.jpg": `${assetsDirectory}/56136f19fc574a3a99761c9a7ce1fcc5149d6edcc60e942cc8e5db2f66e0db91.jpg`, 33 - "Tom_3.jpg": `${assetsDirectory}/b87301f741db5d27e05e8d127ae729af9bb7e2e38484c8f29b52e442c333989d.jpg`, 34 - "Wallace_3.jpg": `${assetsDirectory}/5ba782ee30b8213b554d61adb62091b63c509a539bdd693ed27c1cbc3db40272.jpg`, 35 - "house1.3.jpg": `${assetsDirectory}/9f0121c6a70040d4abbcd41daf909797cef7438f406fc471c0def07f477f920e.jpg`, 36 - "house2.2.jpg": `${assetsDirectory}/461aa813adbb5117b26b791c02864b2e88e6c2899c821f14d58b042c26628b92.jpg`, 37 - "house3.1.jpg": `${assetsDirectory}/269317cfd165abfdbc48e400a6cfc89c2cfcd98a9c738ece5222e8d513bcf83a.jpg`, 38 - "house4.3.jpg": `${assetsDirectory}/a87186061326f3e11259a95ba1229cfc3f1a4f4b06fb50c16ad3757105a2b69c.jpg`, 39 - "house5.2.jpg": `${assetsDirectory}/f78f496ab685b1ae4661c6071358f1a6ab0e1238a0ed1bd157a32317c41a8eaf.jpg`, 40 - "house6.3.jpg": `${assetsDirectory}/b589ac23b4918605f777f45ad32149fa7327fccdd452d4037451c8c28e19c7c0.jpg`, 41 - "house7.1.jpg": `${assetsDirectory}/f528aa7c5e2618c4e8b7ae1e1eced370788e698a339b60be085b46c0044b58e3.jpg`, 42 - "house8.4.jpg": `${assetsDirectory}/28f97e1e523564c8fa7942675ea1609265532de0c715e1b4058cf8ceb4220f9b.jpg`, 43 - "house9.2.jpg": `${assetsDirectory}/6720b60aa89355682657837dca11fc69d684e77cca2cd26029115ac49e940efe.jpg`, 44 - "house10.4.jpg": `${assetsDirectory}/c424a87e1b2220efb59423664e7293de2ed37e60d463bfe3261d8b967183740f.jpg`, 45 - "house11.2.jpg": `${assetsDirectory}/19a8664bab5a3c491510b7f3485d5498c41b680c4695c317be659b4d8e092358.jpg`, 46 - "house12.1.jpg": `${assetsDirectory}/38178a7ec6cc54ed9e61b5fec7790dc85d2debe2dad0b3e960686db660d9428c.jpg`, 47 - "AF0302_1110_00F.jpg": `${assetsDirectory}/70a3f9e412de0797fa723d86ac31bbd4d75746aff3b15bf53bb018fabb33fd9d.jpg`, 48 - "AF0315_1100_00F.jpg": `${assetsDirectory}/1e88f7847b3969fd2b73a7b49d38b4852504c565f173dee03ee13369c613a668.jpg`, 49 - "AF0321_2200_00F.jpg": `${assetsDirectory}/aae1fbd345962daf79d4a6b767b975fd30274c54b5e7309b1e37ca350df93879.jpg`, 50 - "AM0313_1100_00F.jpg": `${assetsDirectory}/488ccf69ae44248357b2b6c8c2a8f6d4179774b61ec88a6df7e8534c95b469c9.jpg`, 51 - "AM0314_1100_00F.jpg": `${assetsDirectory}/bbb27ec39d1cba3fd1f2a4f540e570b3558cb8fd4cbbf321ae87d4842a9f4998.jpg`, 52 - "AM0315_1100_00F.jpg": `${assetsDirectory}/b1f41a2da113c1ba97b45eef80f4eef0408d54b6e59fa1245daf143e17fff480.jpg`, 53 - "AM0318_1100_00F.jpg": `${assetsDirectory}/7c5ac52e4b25464801c1412a3778dc8ebb5e6babe7cc3ef797caab9cae27c197.jpg`, 54 - "AM0319_1100_00F.jpg": `${assetsDirectory}/ea660b6c9f3ec624e236b1618322c47a55647d581953e0cc170897001e23a226.jpg`, 55 - "BF0605_1110_00F.jpg": `${assetsDirectory}/03b688a2807aaa6343710173543419029db8e51715004efeb61bd5eb035c0446.jpg`, 56 - "BF0607_1100_00F.jpg": `${assetsDirectory}/c7f17415c7cfe36c1490bb08612d670e45842f6188deb13e60025d0f9bf7ceb6.jpg`, 57 - "BF0608_1100_00F.jpg": `${assetsDirectory}/31ef42758af1d135fc1d2686d506acb91eac475a207b608dc4f938f400a8e4d4.jpg`, 58 - "BF0624_1100_00F.jpg": `${assetsDirectory}/94d291d64fabe19805a372bbc0f8fbff2ce0cbe97185aa8e163dd6a7bff46524.jpg`, 59 - "BM0606_1100_00F.jpg": `${assetsDirectory}/2677434b03b871c2139762dce2eb843ec1dc1b75e42fea432adfd1888006ad0e.jpg`, 60 - "BM0608_1100_00F.jpg": `${assetsDirectory}/1dd9c91d7bb26a3a51558c1a940327731801bfa977c27334ba97b442a2ee362d.jpg`, 61 - "BM0617_1100_00F.jpg": `${assetsDirectory}/acbb530c69d58f601f66ca34777bf496ab468a86a50f76e5c2c11773e4272f62.jpg`, 62 - "CF0015_1110_00F.jpg": `${assetsDirectory}/5f0d58fd0cd3b2ff39910cf46fc7e55d945b32fb8a278ac22edbec14ef58163c.jpg`, 63 - "CF0042_1100_00F.jpg": `${assetsDirectory}/bd9a46351771da8ef21695b866c7c9a62a64f4dcadba5c0572d181561fdc1d38.jpg`, 64 - "CF0043_1100_00F.jpg": `${assetsDirectory}/1a36367a6a23993bf08c7f8caad7e210549dd8e1a87e6b9912b7629ad5017acc.jpg`, 65 - "CF0046_1100_00F.jpg": `${assetsDirectory}/009e2538252f1302569399283a2878f1d86ed02d89c39bb0b26cc5910a582079.jpg`, 66 - "CF0055_1100_00F.jpg": `${assetsDirectory}/974820af1bd5429c6c43ea754dab319bf3a8dfa542e9a811ecf1eb03734603c8.jpg`, 67 - "CF0056_1110_00F.jpg": `${assetsDirectory}/748f2a112c4fa53bebac4bfe78c34f37c6a31db224771c859a9e51e0a63a4937.jpg`, 68 - "CF0056_3320_00F.jpg": `${assetsDirectory}/32060851f844f9058a1a2776ed95560d1bd3335d337e1dfe447602a1d6fef6ab.jpg`, 69 - "CF0058_1100_00F.jpg": `${assetsDirectory}/e91446554380ed60688ead5fb7aab4d9b397ceb6bdecf12b9f8743d3b058b784.jpg`, 70 - "CM0016_1100_00F.jpg": `${assetsDirectory}/b8902affff25f8061dd97bf4d905e7d480586276ed0630a535b176d6e093da00.jpg`, 71 - "CM0021_1100_00F.jpg": `${assetsDirectory}/d0190be6fcfd09fa786f4e6dff3bcc53974554955e30daa72d48d920f545240d.jpg`, 72 - "CM0024_3100_00F.jpg": `${assetsDirectory}/c0d18474c1dcc6f9d6caabba236da70c49e9166656f9e5b63f74fffe4e80814d.jpg`, 73 - "CM0026_1100_00F.jpg": `${assetsDirectory}/63a86c2f2a060c9444ee619c3cc4d39b1799470ee238e857efa6df20a67a644f.jpg`, 74 - "CM0042_1100_00F.jpg": `${assetsDirectory}/74db8cd385018c3d93975f87c2756cbaab74c9b56655f2b90b5b820894a136f5.jpg`, 75 - "CM0043_1100_00F.jpg": `${assetsDirectory}/f8763af353d54eeb072057bf17b70f9650f204ad086bd2f9a962c5fa51c6f925.jpg`, 76 - "CM0046_1100_00F.jpg": `${assetsDirectory}/14a181fe15d24ba56ceab20c61a4c097982e4aa38b443f6dc66c9222e0f03732.jpg`, 77 - "CM0048_1100_00F.jpg": `${assetsDirectory}/b372d238ccb53efccd09226c9e017076013a827735789ac28edd59e3609ab597.jpg`, 78 - "CM0051_1100_00F.jpg": `${assetsDirectory}/89f220b5b6b83a9df58b485b3cd76c16dad6dc7d0d817f31299ba243e86ae062.jpg`, 79 - "HF1202_1100_00F.jpg": `${assetsDirectory}/5ed0de1a6d92d3ca63ce032ad6285b5f227620ae8ef4c4c0c1c669d9d4bd7ca3.jpg`, 80 - "HF1205_1100_00F.jpg": `${assetsDirectory}/4b5157afe34909cd3588d7c4a06d2071365598ff0495ed2556a1f1cc9218468e.jpg`, 81 - "HF1206_1100_00F.jpg": `${assetsDirectory}/bd8663a2365e03036c62ff178fb60138b7b60c6e77322d2118454751992af602.jpg`, 82 - "HF1209_1100_00F.jpg": `${assetsDirectory}/257cf12508011aff84563c9b3e3e0813ed64a903741149e1b4538bf673d89106.jpg`, 83 - "HF1210_1100_00F.jpg": `${assetsDirectory}/aeca2419dc19debf3046d849e8a06e2b788689c02ad8027ced8a3f2375e2500f.jpg`, 84 - "HF1211_2200_00F.jpg": `${assetsDirectory}/2cfa14ecfa28acea2bbd2edb311875551c8676af978e468c0b052d54c91b9262.jpg`, 85 - "HF1213_1100_00F.jpg": `${assetsDirectory}/eb2a37ebd9eaafc2d7ea9a00dc7a6be843e407d4fadb1cfb17ae5cd58a7b3086.jpg`, 86 - "HF1215_1100_00F.jpg": `${assetsDirectory}/6e2ddbf68d808a85e5dc7fd3406af22bcf38c5923d4ecbb6c1c4db7e8ba2de95.jpg`, 87 - "HF1216_2220_00F.jpg": `${assetsDirectory}/0df04c58e1531ea697b0708731f901c2a06408209244de8c8ab1859330f90948.jpg`, 88 - "HM1201_3101_00F.jpg": `${assetsDirectory}/8a86092e8797bb6116e68c6252fd96f1a807dc12bf38128a23b9a13ed6940a56.jpg`, 89 - "MF0908_1100_00F.jpg": `${assetsDirectory}/099ff3b2e3ec46af119f39fba91e436d8ec2f488ba1084ae1c91ca99d30a86ac.jpg`, 90 - "MF0909_1100_00F.jpg": `${assetsDirectory}/3b1c8917d8128434d8819bb7c7daeca03afa0fa2d6b268e617385e8bd411f188.jpg`, 91 - "MF0912_1100_00F.jpg": `${assetsDirectory}/ccbb7f216a8f0dd084895791d5638ac1f76549fac047bd2a1483a7a8aa0d6603.jpg`, 92 - "MF0915_1100_00F.jpg": `${assetsDirectory}/5b2b2d8213a53028b1715442d717c1bdab9cb8ed317fced7fe5c21c23ff5f292.jpg`, 93 - "MF0918_1100_00F.jpg": `${assetsDirectory}/cc31283d02899710fc81a2f22876d67193b961644b7e07f562748289330df0f0.jpg`, 94 - "MF0918_1100_30L.jpg": `${assetsDirectory}/5b697d98b0115ba9a7439108d74087237e2fd70a59266487b053fc07d7365f42.jpg`, 95 - "MF0918_2200_00F.jpg": `${assetsDirectory}/dbca65a369505941349cbfee2c40b424bef24b16f6ca8da4c885e76da538d439.jpg`, 96 - "MM0901_1100_00F.jpg": `${assetsDirectory}/3e291ae00783cadfc1b258aa308cec137ee3c6a312dbf0b44a62bd21e19046d0.jpg`, 97 - "MM0905_1110_00F.jpg": `${assetsDirectory}/8b0eb6baf2e6c016cf6c78424a0c405c093a64ddcbd6293bdaac4ad25cdf6ef1.jpg` 98 - }, 18 + "files": {}, 99 19 "parameters": {}, 100 20 "responses": {}, 101 21 "messageHandlers": {}, ··· 115 35 }, 116 36 { 117 37 "type": "lab.canvas.Frame", 118 - "context": "\u003Cmain class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Ccanvas \u002F\u003E\n\u003C\u002Fmain\u003E\n\n\u003Cfooter class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Cp\u003E\n Press \u003Ckbd\u003E1\u003C\u002Fkbd\u003E for a face and \u003Ckbd\u003E9\u003C\u002Fkbd\u003E for a house. \n \u003C\u002Fp\u003E\n\u003C\u002Ffooter\u003E", 38 + "context": "\u003Cmain class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Ccanvas \u002F\u003E\n\u003C\u002Fmain\u003E\n\n\u003Cfooter class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Cp\u003E\n ${this.parameters.taskHelp} \n \u003C\u002Fp\u003E\n\u003C\u002Ffooter\u003E", 119 39 "contextSelector": "canvas", 120 40 "files": {}, 121 41 "parameters": {}, ··· 128 48 "type": "lab.flow.Loop", 129 49 "files": {}, 130 50 "parameters": {}, 131 - "templateParameters": [ 132 - { 133 - "condition": "Face", 134 - "image": "Annie_3.jpg", 135 - "correctResponse": "1", 136 - "phase": "practice" 137 - }, 138 - { 139 - "condition": "Face", 140 - "image": "Joan_3.jpg", 141 - "correctResponse": "1", 142 - "phase": "practice" 143 - }, 144 - { 145 - "condition": "Face", 146 - "image": "Jodi_3.jpg", 147 - "correctResponse": "1", 148 - "phase": "practice" 149 - }, 150 - { 151 - "condition": "House", 152 - "image": "house1.3.jpg", 153 - "correctResponse": "9", 154 - "phase": "practice" 155 - }, 156 - { 157 - "condition": "House", 158 - "image": "house2.2.jpg", 159 - "correctResponse": "9", 160 - "phase": "practice" 161 - }, 162 - { 163 - "condition": "House", 164 - "image": "house3.1.jpg", 165 - "correctResponse": "9", 166 - "phase": "practice" 167 - } 168 - ], 51 + "templateParameters": [], 169 52 "sample": { 170 - "mode": "draw-shuffle", 53 + "mode": "sequential", 171 54 "n": "" 172 55 }, 173 56 "responses": {}, 174 - "messageHandlers": {}, 57 + "messageHandlers": { 58 + "before:prepare": function anonymous() { 59 + let initParameters = [...this.parameters.stimuli] || []; 60 + initParameters = initParameters.filter(t => t.phase === 'practice') || []; 61 + let numberTrials = this.parameters.nbPracticeTrials; 62 + if(initParameters.length === 0){ 63 + numberTrials = 0; 64 + } 65 + const randomize = this.parameters.randomize; 66 + const trialsLength = initParameters.length; 67 + if(numberTrials > trialsLength){ 68 + const append = [...initParameters] 69 + const multiply = Math.ceil(numberTrials / trialsLength); 70 + for (let i = 0; i < multiply; i++){ 71 + initParameters = initParameters.concat(append) 72 + } 73 + } 74 + 75 + function shuffle(a) { 76 + let j, x, i 77 + for (i = a.length - 1; i > 0; i--) { 78 + j = Math.floor(Math.random() * (i + 1)) 79 + x = a[i] 80 + a[i] = a[j] 81 + a[j] = x 82 + } 83 + return a 84 + } 85 + 86 + if(randomize === 'random'){ 87 + shuffle(initParameters); 88 + } 89 + 90 + const trialConstructor = ( file ) => ({ 91 + 'condition': file.condition, 92 + 'image': file.filename, 93 + 'correctResponse': file.response, 94 + 'phase': 'practice', 95 + 'name': file.name, 96 + 'type': file.type, 97 + }) 98 + 99 + // balance design across conditions 100 + const conditions = Array.from(new Set(initParameters.map(p => p.condition))); 101 + const conditionsParameters = {}; 102 + for (const c of conditions) { 103 + conditionsParameters[c] = initParameters.filter(p => p.condition == c) 104 + } 105 + const numberConditionsTrials = Math.ceil(numberTrials / conditions.length); 106 + let balancedParameters = []; 107 + for (let i = 0; i < numberConditionsTrials; i++){ 108 + for (const c of conditions) { 109 + balancedParameters = balancedParameters.concat(conditionsParameters[c][i]) 110 + } 111 + } 112 + initParameters = [...balancedParameters.slice(0, numberTrials)]; 113 + 114 + let practiceParameters = []; 115 + for (let i = 0; i < numberTrials; i++){ 116 + practiceParameters = practiceParameters.concat(trialConstructor(initParameters[i])) 117 + } 118 + 119 + // assign options values to parameters of this task 120 + this.options.templateParameters = practiceParameters; 121 + if(randomize === 'random'){ 122 + this.options.shuffle = true; 123 + } else { 124 + this.options.shuffle = false; 125 + } 126 + } 127 + }, 175 128 "title": "Practice loop", 176 129 "shuffleGroups": [], 177 130 "template": { ··· 328 281 // On a keydown event, we record the key and the time of response. 329 282 // We also record whether the response was correct (by comparing the pressed key with the correct response which is defined inside the Experiment loop). 330 283 // "this" in the code means the lab.js experiment. 284 + const responses = [... new Set(this.parameters.stimuli.map( e => (e.response)))]; 331 285 this.data.trial_number = 1 + parseInt(this.options.id.split('_')[this.options.id.split('_').length-2]); 332 286 this.data.response_given = 'no'; 333 287 334 288 this.options.events = { 335 289 'keydown': (event) => { 336 - if(['Digit1', 'Digit9'].includes(event.code)){ 290 + if(responses.includes(event.key)){ 337 291 this.data.reaction_time = this.timer; 338 292 if(this.parameters.phase === 'task') this.data.response_given = 'yes'; 339 293 this.data.response = event.key; ··· 352 306 800, 353 307 600 354 308 ], 355 - "title": "Stimulus" 309 + "title": "Stimulus", 310 + "timeout": "${parameters.selfPaced ? '3600000' : parameters.presentationTime}", 356 311 }, 357 312 { 358 313 "type": "lab.canvas.Screen", ··· 436 391 }, 437 392 { 438 393 "type": "lab.canvas.Frame", 439 - "context": "\u003Cmain class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Ccanvas \u002F\u003E\n\u003C\u002Fmain\u003E\n\n\u003Cfooter class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Cp\u003E\n Press \u003Ckbd\u003E1\u003C\u002Fkbd\u003E for a face and \u003Ckbd\u003E9\u003C\u002Fkbd\u003E for a house. \n \u003C\u002Fp\u003E\n\u003C\u002Ffooter\u003E", 394 + "context": "\u003Cmain class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Ccanvas \u002F\u003E\n\u003C\u002Fmain\u003E\n\n\u003Cfooter class=\"content-vertical-center content-horizontal-center\"\u003E\n \u003Cp\u003E\n ${this.parameters.taskHelp} \n \u003C\u002Fp\u003E\n\u003C\u002Ffooter\u003E", 440 395 "contextSelector": "canvas", 441 396 "files": {}, 442 397 "parameters": {}, ··· 447 402 "type": "lab.flow.Loop", 448 403 "files": {}, 449 404 "parameters": {}, 450 - "templateParameters": [ 451 - { 452 - "condition": "Face", 453 - "image": "AF0302_1110_00F.jpg", 454 - "correctResponse": "1", 455 - "phase": "task" 456 - }, 457 - { 458 - "condition": "Face", 459 - "image": "AF0315_1100_00F.jpg", 460 - "correctResponse": "1", 461 - "phase": "task" 462 - }, 463 - { 464 - "condition": "Face", 465 - "image": "AF0321_2200_00F.jpg", 466 - "correctResponse": "1", 467 - "phase": "task" 468 - }, 469 - { 470 - "condition": "Face", 471 - "image": "AM0313_1100_00F.jpg", 472 - "correctResponse": "1", 473 - "phase": "task" 474 - }, 475 - { 476 - "condition": "Face", 477 - "image": "AM0314_1100_00F.jpg", 478 - "correctResponse": "1", 479 - "phase": "task" 480 - }, 481 - { 482 - "condition": "Face", 483 - "image": "AM0315_1100_00F.jpg", 484 - "correctResponse": "1", 485 - "phase": "task" 486 - }, 487 - { 488 - "condition": "Face", 489 - "image": "AM0318_1100_00F.jpg", 490 - "correctResponse": "1", 491 - "phase": "task" 492 - }, 493 - { 494 - "condition": "Face", 495 - "image": "AM0319_1100_00F.jpg", 496 - "correctResponse": "1", 497 - "phase": "task" 498 - }, 499 - { 500 - "condition": "Face", 501 - "image": "BF0605_1110_00F.jpg", 502 - "correctResponse": "1", 503 - "phase": "task" 504 - }, 505 - { 506 - "condition": "Face", 507 - "image": "BF0607_1100_00F.jpg", 508 - "correctResponse": "1", 509 - "phase": "task" 510 - }, 511 - { 512 - "condition": "Face", 513 - "image": "BF0608_1100_00F.jpg", 514 - "correctResponse": "1", 515 - "phase": "task" 516 - }, 517 - { 518 - "condition": "Face", 519 - "image": "BF0624_1100_00F.jpg", 520 - "correctResponse": "1", 521 - "phase": "task" 522 - }, 523 - { 524 - "condition": "House", 525 - "image": "house1.3.jpg", 526 - "correctResponse": "9", 527 - "phase": "task" 528 - }, 529 - { 530 - "condition": "House", 531 - "image": "house2.2.jpg", 532 - "correctResponse": "9", 533 - "phase": "task" 534 - }, 535 - { 536 - "condition": "House", 537 - "image": "house3.1.jpg", 538 - "correctResponse": "9", 539 - "phase": "task" 540 - }, 541 - { 542 - "condition": "House", 543 - "image": "house4.3.jpg", 544 - "correctResponse": "9", 545 - "phase": "task" 546 - }, 547 - { 548 - "condition": "House", 549 - "image": "house5.2.jpg", 550 - "correctResponse": "9", 551 - "phase": "task" 552 - }, 553 - { 554 - "condition": "House", 555 - "image": "house6.3.jpg", 556 - "correctResponse": "9", 557 - "phase": "task" 558 - }, 559 - { 560 - "condition": "House", 561 - "image": "house7.1.jpg", 562 - "correctResponse": "9", 563 - "phase": "task" 564 - }, 565 - { 566 - "condition": "House", 567 - "image": "house8.4.jpg", 568 - "correctResponse": "9", 569 - "phase": "task" 570 - }, 571 - { 572 - "condition": "House", 573 - "image": "house9.2.jpg", 574 - "correctResponse": "9", 575 - "phase": "task" 576 - }, 577 - { 578 - "condition": "House", 579 - "image": "house10.4.jpg", 580 - "correctResponse": "9", 581 - "phase": "task" 582 - }, 583 - { 584 - "condition": "House", 585 - "image": "house11.2.jpg", 586 - "correctResponse": "9", 587 - "phase": "task" 588 - }, 589 - { 590 - "condition": "House", 591 - "image": "house12.1.jpg", 592 - "correctResponse": "9", 593 - "phase": "task" 594 - } 595 - ], 405 + "templateParameters": [], 596 406 "sample": { 597 - "mode": "draw-shuffle", 598 - "n": "96" 407 + "mode": "sequential", 408 + "n": "" 599 409 }, 600 410 "responses": {}, 601 - "messageHandlers": {}, 411 + "messageHandlers": { 412 + "before:prepare": function anonymous() { 413 + let initialParameters = [...this.parameters.stimuli] || []; 414 + initialParameters = initialParameters.filter(t => t.phase === 'main') || []; 415 + let numberTrials = this.parameters.nbTrials; 416 + if(initialParameters.length === 0){ 417 + numberTrials = 0; 418 + } 419 + const randomize = this.parameters.randomize; 420 + const trialsLength = initialParameters.length; 421 + if(numberTrials > trialsLength){ 422 + const append = [...initialParameters] 423 + const multiply = Math.ceil(numberTrials / trialsLength); 424 + for (let i = 0; i < multiply; i++){ 425 + initialParameters = initialParameters.concat(append) 426 + } 427 + } 428 + 429 + function shuffle(a) { 430 + let j, x, i 431 + for (i = a.length - 1; i > 0; i--) { 432 + j = Math.floor(Math.random() * (i + 1)) 433 + x = a[i] 434 + a[i] = a[j] 435 + a[j] = x 436 + } 437 + return a 438 + } 439 + 440 + if(randomize === 'random'){ 441 + shuffle(initialParameters); 442 + } 443 + 444 + const trialConstructor = ( file ) => ({ 445 + 'condition': file.condition, 446 + 'image': file.filename, 447 + 'correctResponse': file.response, 448 + 'phase': 'task', 449 + 'name': file.name, 450 + 'type': file.type, 451 + }) 452 + // balance design across conditions 453 + const conditions = Array.from(new Set(initialParameters.map(p => p.condition))); 454 + const conditionsParameters = {}; 455 + for (const c of conditions) { 456 + conditionsParameters[c] = initialParameters.filter(p => p.condition == c) 457 + } 458 + const numberConditionsTrials = Math.ceil(numberTrials / conditions.length); 459 + let balancedParameters = []; 460 + for (let i = 0; i < numberConditionsTrials; i++){ 461 + for (const c of conditions) { 462 + balancedParameters = balancedParameters.concat(conditionsParameters[c][i]) 463 + } 464 + } 465 + initialParameters = [...balancedParameters.slice(0, numberTrials)]; 466 + 467 + let trialParameters = []; 468 + for (let i = 0; i < numberTrials; i++){ 469 + trialParameters = [...trialParameters.concat(trialConstructor(initialParameters[i]))] 470 + } 471 + // assign options values to parameters of this task 472 + this.options.templateParameters = trialParameters; 473 + if(randomize === 'random'){ 474 + this.options.shuffle = true; 475 + } else { 476 + this.options.shuffle = false; 477 + } 478 + 479 + } 480 + }, 602 481 "title": "Experiment loop", 482 + "tardy": true, 603 483 "shuffleGroups": [], 604 484 "template": { 605 485 "type": "lab.flow.Sequence", ··· 749 629 "responses": {}, 750 630 "messageHandlers": { 751 631 "before:prepare": function anonymous( 752 - ) { 753 - // This code registers an event listener for this screen. 754 - // We have a timeout for this screen, but we also want to record responses. 755 - // On a keydown event, we record the key and the time of response. 756 - // We also record whether the response was correct (by comparing the pressed key with the correct response which is defined inside the Experiment loop). 757 - // "this" in the code means the lab.js experiment. 758 - this.data.trial_number = 1 + parseInt(this.options.id.split('_')[this.options.id.split('_').length-2]); 759 - this.data.response_given = 'no'; 632 + ) { 633 + // This code registers an event listener for this screen. 634 + // On a keydown event, we record the key and the time of response. 635 + // We also record whether the response was correct (by comparing the pressed key with the correct response which is defined inside the Experiment loop). 636 + // "this" in the code means the lab.js experiment. 637 + // extract the possible responses from the parameters 638 + const responses = [... new Set(this.parameters.stimuli.map( e => (e.response)))]; 639 + this.data.trial_number = 1 + parseInt(this.options.id.split('_')[this.options.id.split('_').length-2]); 640 + this.data.response_given = 'no'; 760 641 761 - this.options.events = { 762 - 'keydown': (event) => { 763 - if(['Digit1', 'Digit9'].includes(event.code)){ 764 - this.data.reaction_time = this.timer; 765 - if(this.parameters.phase === 'task') this.data.response_given = 'yes'; 766 - this.data.response = event.key; 767 - if(this.data.response == this.parameters.correctResponse){ 768 - this.data.correct_response = true; 769 - } else { 770 - this.data.correct_response = false; 771 - } 772 - this.end(); 773 - } 774 - } 775 - } 776 - }, 777 - 'run': function anonymous() { 778 - this.parameters.callbackForEEG(this.parameters.condition === 'Face' ? 2 : 1); 779 - } 642 + this.options.events = { 643 + 'keydown': (event) => { 644 + if(responses.includes(event.key)){ 645 + this.data.reaction_time = this.timer; 646 + if(this.parameters.phase === 'task') this.data.response_given = 'yes'; 647 + this.data.response = event.key; 648 + if(this.data.response == this.parameters.correctResponse){ 649 + this.data.correct_response = true; 650 + } else { 651 + this.data.correct_response = false; 652 + } 653 + this.end(); 654 + } 655 + } 656 + } 657 + }, 658 + 'run': function anonymous() { 659 + this.parameters.callbackForEEG(this.parameters.type); 660 + } 780 661 }, 781 662 "viewport": [ 782 663 800, 783 664 600 784 665 ], 785 - "title": "Stimulus" 666 + "title": "Stimulus", 667 + "timeout": "${parameters.selfPaced ? '3600000' : parameters.presentationTime}", 786 668 }, 787 669 { 788 670 "type": "lab.canvas.Screen",
+1
package.json
··· 236 236 "lodash": "^4.17.15", 237 237 "lodash.clonedeep": "^4.5.0", 238 238 "mkdirp": "^0.5.1", 239 + "moment": "^2.24.0", 239 240 "mousetrap": "^1.6.2", 240 241 "muse-js": "^3.1.0", 241 242 "papaparse": "^5.0.0",