import { JsPsych , JsPsychExtension , JsPsychExtensionInfo } from "jspsych" ; interface InitializeParameters { default minimum_sample_time : number ; } interface OnStartParameters { /**
     * An array of string selectors. The selectors should identify one unique element on the page.
     * The DOMRect of the element will be stored in the data.
     */ targets ? : Array < string > ; default events ? : Array < string > ; } class MouseTrackingExtension implements JsPsychExtension { static info : JsPsychExtensionInfo = { name : "mouse-tracking" , } ; constructor ( private jsPsych ) { } private domObserver : MutationObserver ; private currentTrialData : Array < object > ; private currentTrialTargets : Map < string , DOMRect > ; private currentTrialSelectors : Array < string > ; private currentTrialStartTime : number ; private minimumSampleTime : number ; private lastSampleTime : number ; private eventsToTrack : Array < string > ; initialize = async ( { minimum_sample_time = 0 } ) => { this . domObserver = new MutationObserver ( this . mutationObserverCallback ) ; this . minimumSampleTime = minimum_sample_time ; } ; on_start = ( params ) : void => { params = params || { } ; this . currentTrialData = [ ] ; this . currentTrialTargets = new Map ( ) ; this . currentTrialSelectors = params . targets || [ ] ; this . lastSampleTime = null ; this . eventsToTrack = params . events || [ "mousemove" ] ; this . domObserver . observe ( this . jsPsych . getDisplayElement ( ) , { childList : true } ) ; } ; on_load = ( ) => { this . currentTrialStartTime = performance . now ( ) ; if ( this . eventsToTrack . includes ( "mousemove" ) ) { window . addEventListener ( "mousemove" , this . mouseMoveEventHandler ) ; } if ( this . eventsToTrack . includes ( "mousedown" ) ) { window . addEventListener ( "mousedown" , this . mouseDownEventHandler ) ; } if ( this . eventsToTrack . includes ( "mouseup" ) ) { window . addEventListener ( "mouseup" , this . mouseUpEventHandler ) ; } } ; on_finish = ( ) => { this . domObserver . disconnect ( ) ; if ( this . eventsToTrack . includes ( "mousemove" ) ) { window . removeEventListener ( "mousemove" , this . mouseMoveEventHandler ) ; } if ( this . eventsToTrack . includes ( "mousedown" ) ) { window . removeEventListener ( "mousedown" , this . mouseDownEventHandler ) ; } if ( this . eventsToTrack . includes ( "mouseup" ) ) { window . removeEventListener ( "mouseup" , this . mouseUpEventHandler ) ; } return { mouse_tracking_data : this . currentTrialData , mouse_tracking_targets : Object . fromEntries ( this . currentTrialTargets . entries ( ) ) , } ; } ; private mouseMoveEventHandler = ( { clientX : x , clientY : y } ) => { const event_time = performance . now ( ) ; const t = Math . round ( event_time - this . currentTrialStartTime ) ; if ( this . lastSampleTime === null || event_time - this . lastSampleTime >= this . minimumSampleTime ) { this . lastSampleTime = event_time ; this . currentTrialData . push ( { x , y , t , event : "mousemove" } ) ; } } ; private mouseUpEventHandler = ( { clientX : x , clientY : y } ) => { const event_time = performance . now ( ) ; const t = Math . round ( event_time - this . currentTrialStartTime ) ; this . currentTrialData . push ( { x , y , t , event : "mouseup" } ) ; } ; private mouseDownEventHandler = ( { clientX : x , clientY : y } ) => { const event_time = performance . now ( ) ; const t = Math . round ( event_time - this . currentTrialStartTime ) ; this . currentTrialData . push ( { x , y , t , event : "mousedown" } ) ; } ; private mutationObserverCallback = ( mutationsList , observer ) => { for ( const selector of this . currentTrialSelectors ) { if ( ! this . currentTrialTargets . has ( selector ) ) { const target = this . jsPsych . getDisplayElement ( ) . querySelector ( selector ) ; if ( target ) { this . currentTrialTargets . set ( selector , target . getBoundingClientRect ( ) ) ; } } } } ; } export default MouseTrackingExtension ;