import * as React from "react" ; import { AnalyzerFrame } from "./analyzerTools" ; import { unreachable } from "./analyzerTools" ; import { Checkbox , Dialog , DialogContent , Drawer , FormControlLabel , FormGroup , IconButton , Paper , Toolbar , Tooltip } from "@material-ui/core" ; import { grey } from "@material-ui/core/colors" ; import DashboardIcon from "@material-ui/icons/Dashboard" ; import ListIcon from "@material-ui/icons/List" ; import SkipPreviousIcon from "@material-ui/icons/SkipPrevious" ; import StopIcon from "@material-ui/icons/Stop" ; import PlayArrowIcon from "@material-ui/icons/PlayArrow" ; import SkipNextIcon from "@material-ui/icons/SkipNext" ; import Replay30Icon from "@material-ui/icons/Replay30" ; declare const Mousetrap ; enum SplitMode { Vertical , Horizontal , Last } interface SplitViewProps { groups : AnalyzerFrame [ ] [ ] ; groupNames ? : string [ ] ; onDecodeAdditionalFrames : ( count : number ) => void ; playbackFrameRate ? : number ; mode ? : SplitMode ; } export class SplitView extends React . Component < SplitViewProps , { activeFrame : number ; mode : SplitMode ; playInterval : any ; lockScroll : boolean ; showDetails : boolean ; } > { center : HTMLDivElement ; left : HTMLDivElement ; right : HTMLDivElement ; top : HTMLDivElement ; bottom : HTMLDivElement ; public static defaultProps : SplitViewProps = { groups : [ ] , groupNames : null , playbackFrameRate : 30 , onDecodeAdditionalFrames : null , mode : SplitMode . Vertical } ; constructor ( props ) { super ( props ) ; this . state = { activeFrame : 0 , playInterval : null , lockScroll : false , mode : props . mode % SplitMode . Last , showDetails : false } ; } componentDidMount ( ) { this . installKeyboardShortcuts ( ) ; } installKeyboardShortcuts ( ) { Mousetrap . bind ( [ '`' ] , ( e ) => { this . toggleDetails ( ) ; e . preventDefault ( ) ; } ) ; Mousetrap . bind ( [ 'tab' ] , ( e ) => { this . advanceView ( ) ; e . preventDefault ( ) ; } ) ; Mousetrap . bind ( [ 'space' ] , ( e ) => { this . playPause ( ) ; e . preventDefault ( ) ; } ) ; Mousetrap . bind ( [ '.' ] , ( e ) => { this . advanceFrame ( 1 ) ; e . preventDefault ( ) ; } ) ; Mousetrap . bind ( [ ',' ] , ( ) => { this . advanceFrame ( - 1 ) ; } ) ; } mountView ( name , el , group ) { this [ name ] = el ; let self = this ; function lock ( a , b ) { if ( self . state . lockScroll ) { self [ a ] . scrollTop = self [ b ] . scrollTop ; self [ a ] . scrollLeft = self [ b ] . scrollLeft ; } } if ( this [ name ] ) { if ( name === "left" ) { el . onscroll = lock . bind ( this , "right" , "left" ) ; } else if ( name === "right" ) { el . onscroll = lock . bind ( this , "left" , "right" ) ; } else if ( name === "top" ) { el . onscroll = lock . bind ( this , "bottom" , "top" ) ; } else if ( name === "bottom" ) { el . onscroll = lock . bind ( this , "top" , "bottom" ) ; } if ( el . lastChild ) { el . removeChild ( el . lastChild ) ; } el . appendChild ( this . props . groups [ group ] [ this . state . activeFrame ] . image ) ; } } toggleDetails ( ) { this . setState ( { showDetails : ! this . state . showDetails } as any ) ; } advanceView ( ) { let mode = this . state . mode ; mode = ( mode + 1 ) % SplitMode . Last ; this . setState ( { mode } as any ) ; } advanceFrame ( delta ) { let activeFrame = this . state . activeFrame + delta ; if ( activeFrame < 0 ) { activeFrame += this . props . groups [ 0 ] . length ; } activeFrame = activeFrame % this . props . groups [ 0 ] . length ; this . setActiveFrame ( activeFrame ) ; } setActiveFrame ( activeFrame ) { this . setState ( { activeFrame } as any ) ; } playPause ( ) { let playInterval = this . state . playInterval ; if ( ! playInterval ) { playInterval = setInterval ( ( ) => { this . advanceFrame ( 1 ) ; } , 1000 / this . props . playbackFrameRate ) ; } else { clearInterval ( playInterval ) ; playInterval = 0 ; } this . setState ( { playInterval } as any ) ; } alertDecodeAdditionalFrames ( count ) { alert ( "Frames will be decoded in the background and may take a while." ) ; if ( this . props . onDecodeAdditionalFrames ) { this . props . onDecodeAdditionalFrames ( count ) ; } } render ( ) { if ( this . props . groups . length < 2 ) { return < Dialog open = { true } > < DialogContent > Provide at least two videos to compare . < /DialogContent></Dialog > ; } let content = null ; switch ( this . state . mode ) { case SplitMode . Vertical : content = < div className = "splitVerticalContainer" > < div className = "splitVerticalContent" ref = { ( self ) => this . mountView ( "left" , self , 0 ) } /> < div className = "splitVerticalContent" ref = { ( self ) => this . mountView ( "right" , self , 1 ) } /> < /div>; break ; case SplitMode . Horizontal : content = < div className = "splitHorizontalContainer" > < div className = "splitHorizontalContent" ref = { ( self ) => this . mountView ( "top" , self , 0 ) } /> < div className = "splitHorizontalContent" ref = { ( self ) => this . mountView ( "bottom" , self , 1 ) } /> < /div>; break ; } let details = null ; const paperStyle = { padding : 10 , marginTop : 10 , marginBottom : 10 , backgroundColor : grey [ 900 ] } ; let self = this ; function getLabel ( i ) { switch ( self . state . mode ) { case SplitMode . Vertical : return [ "Left" , "Right" , "" ] [ i ] ; case SplitMode . Horizontal : return [ "Top" , "Bottom" , "" ] [ i ] ; default : unreachable ( ) ; } } function getGroupName ( i ) { let groupNames = self . props . groupNames ; if ( groupNames && groupNames . length ) { return groupNames [ i ] ; } return "Untitled " + i ; } if ( this . props . groups ) { details = < div className = "splitTextContent" > { this . props . groups . map ( ( name , i ) => < Paper key = { i } style = { paperStyle } > < div > { getLabel ( i ) } { getGroupName ( i ) } < /div> < /Paper>)} < /div>; } return < div className = "maxWidthAndHeight" > < Drawer style = { { width : '512px' } } open = { this . state . showDetails } onClose = { ( ) => this . setState ( state => ( { showDetails : ! state . showDetails } ) ) } > { details } < /Drawer> { content } < Toolbar > < div > < Tooltip title = "Toggle Details: `" placement = "top-end" > < IconButton onClick = { this . toggleDetails . bind ( this ) } > < ListIcon /> < /IconButton> < /Tooltip> < Tooltip title = "Toggle View Mode: tab" placement = "top" > < IconButton onClick = { this . advanceView . bind ( this ) } > < DashboardIcon /> < /IconButton> < /Tooltip> < Tooltip title = "Previous: ," placement = "top" > < IconButton onClick = { this . advanceFrame . bind ( this , - 1 ) } > < SkipPreviousIcon /> < /IconButton> < /Tooltip> < Tooltip title = "Pause / Play: space" placement = "top" > < IconButton onClick = { this . playPause . bind ( this ) } > { ! this . state . playInterval ? < PlayArrowIcon /> : <StopIcon / > } < /IconButton> < /Tooltip> < Tooltip title = "Next: ." placement = "top" > < IconButton onClick = { this . advanceFrame . bind ( this , 1 ) } > < SkipNextIcon /> < /IconButton> < /Tooltip> < Tooltip title = "Decode 30 Additional Frames" placement = "top" > < IconButton onClick = { this . alertDecodeAdditionalFrames . bind ( this , 30 ) } > < Replay30Icon /> < /IconButton> < /Tooltip> < span className = "splitTextContent" style = { { width : "256px" } } > Frame : { this . state . activeFrame + 1 } of { this . props . groups [ 0 ] . length } < /span> < FormGroup row > < FormControlLabel control = { < Checkbox checked = { this . state . lockScroll } onChange = { ( event ) => this . setState ( { lockScroll : event . target . checked } ) } />} label="Lock Scroll"/ > < /FormGroup> < /div> < /Toolbar> < /div>; } }