import { URLSearchParams } from '@angular/http' ; import { Client , StringReader , Tunnel } from '@illgrenoble/guacamole-common-js' ; import { BehaviorSubject , ReplaySubject , Subject } from 'rxjs' ; /**
 * Manages the connection to the remote desktop
 */ export class RemoteDesktopManager { static STATE = { /**
         * The machine connection has not yet been attempted.
         */ IDLE : 'IDLE' , /**
         * The machine connection is being established.
         */ CONNECTING : 'CONNECTING' , /**
         * The machine connection has been successfully established, and the
         * client is now waiting for receipt of initial graphical data.
         */ WAITING : 'WAITING' , /**
         * The Guacamole connection has been successfully established, and
         * initial graphical data has been received.
         */ CONNECTED : 'CONNECTED' , /**
         * The machine connection has terminated successfully. No errors are
         * indicated.
         */ DISCONNECTED : 'DISCONNECTED' , /**
         * The machine connection has terminated due to an error reported by
         * the client. The associated error code is stored in statusCode.
         *
         */ CLIENT_ERROR : 'CLIENT_ERROR' , /**
         * The machine connection has terminated due to an error reported by
         * the tunnel. The associated error code is stored in statusCode.
         */ TUNNEL_ERROR : 'TUNNEL_ERROR' } ; /**
     * Remote desktop connection state observable
     * Subscribe to this if you want to be notified when the connection state changes
     */ public onStateChange = new BehaviorSubject ( RemoteDesktopManager . STATE . IDLE ) ; /**
     * Remote desktop clipboard observable.
     * Subscribe to this if you want to be notified if text has been cut/copied within
     * the remote desktop.
     */ public onRemoteClipboardData = new ReplaySubject ( 1 ) ; public onKeyboardReset = new BehaviorSubject < boolean > ( true ) ; public onFocused = new BehaviorSubject < boolean > ( true ) ; public onFullScreen = new BehaviorSubject < boolean > ( false ) ; public onReconnect = new Subject < boolean > ( ) ; /**
     * When an instruction is received from the tunnel
     */ public onTunnelInstruction = new BehaviorSubject < { opcode : string ; parameters : any ; } > ( null ) ; /**
     * The actual underlying remote desktop client
     */ private client : Client ; /**
     * The tunnel being used by the underlying remote desktop client
     */ private tunnel : Tunnel ; param tunnel param parameters constructor ( tunnel ) { this . tunnel = tunnel ; this . client = new Client ( this . tunnel ) ; } /**
     * Get the guacamole connection state
     */ public getState ( ) { return this . onStateChange . getValue ( ) ; } param state public isState ( state ) { return state === this . onStateChange . getValue ( ) ; } param newFocused public setFocused ( newFocused ) { this . onFocused . next ( newFocused ) ; } param newFullScreen public setFullScreen ( newFullScreen ) { this . onFullScreen . next ( newFullScreen ) ; } /**
     * Is the display full screen?
     */ public isFullScreen ( ) { return this . onFullScreen . getValue ( ) ; } /**
     * Is the tunnel connected?
     */ public isConnected ( ) { return this . onStateChange . getValue ( ) === RemoteDesktopManager . STATE . CONNECTED ; } /**
     * Get the guacamole client
     */ public getClient ( ) { return this . client ; } /**
     * Get the guacamole tunnel
     */ public getTunnel ( ) { return this . tunnel ; } param { number } width param { number } height returns { string } public createThumbnail ( width = 340 , height = 240 ) { const display = this . client . getDisplay ( ) ; if ( display && display . getWidth ( ) > 0 && display . getHeight ( ) > 0 ) { const canvas = display . flatten ( ) ; const scale = Math . min ( width / canvas . width , height / canvas . height , 1 ) ; const thumbnail = document . createElement ( 'canvas' ) ; thumbnail . width = canvas . width * scale ; thumbnail . height = canvas . height * scale ; const context = thumbnail . getContext ( '2d' ) ; context . drawImage ( canvas , 0 , 0 , canvas . width , canvas . height , 0 , 0 , thumbnail . width , thumbnail . height ) ; return thumbnail . toDataURL ( 'image/png' ) ; } return null ; } param { blob } done public createScreenshot ( done ) { const display = this . client . getDisplay ( ) ; if ( display && display . getWidth ( ) > 0 && display . getHeight ( ) > 0 ) { const canvas = display . flatten ( ) ; return canvas . toBlob ( done ) ; } done ( null ) ; } param { string } text public sendRemoteClipboardData ( text ) { if ( text ) { this . onRemoteClipboardData . next ( text ) ; this . client . setClipboard ( text ) ; } } /**
     * Reset the keyboard
     * This will release all keys
     */ public resetKeyboard ( ) { this . onKeyboardReset . next ( true ) ; } /**
     * Disconnect from the remote desktop
     */ public disconnect ( ) { this . client . disconnect ( ) ; } /**
     * Connect to the remote desktop
     */ public connect ( parameters = { } ) { const configuration = this . buildParameters ( parameters ) ; this . client . connect ( configuration ) ; this . bindEventHandlers ( ) ; } param state private setState ( state ) { this . onStateChange . next ( state ) ; } param stream param mimetype private handleClipboard ( stream , mimetype ) { if ( /^text\// . exec ( mimetype ) ) { const reader = new StringReader ( stream ) ; let data = '' ; reader . ontext = ( text ) => data += text ; reader . onend = ( ) => this . onRemoteClipboardData . next ( data ) ; } } /**
     * Build the URL query parameters to send to the tunnel connection
     */ private buildParameters ( parameters = { } ) { const params = new URLSearchParams ( ) ; for ( const key in parameters ) { if ( parameters . hasOwnProperty ( key ) ) { params . set ( key , parameters [ key ] ) ; } } return params . toString ( ) ; } /**
     * Bind the client and tunnel event handlers
     */ private bindEventHandlers ( ) { this . client . onerror = this . handleClientError . bind ( this ) ; this . client . onstatechange = this . handleClientStateChange . bind ( this ) ; this . client . onclipboard = this . handleClipboard . bind ( this ) ; this . tunnel . onerror = this . handleTunnelError . bind ( this ) ; this . tunnel . onstatechange = this . handleTunnelStateChange . bind ( this ) ; this . tunnel . oninstruction = ( ( oninstruction ) => { return ( opcode , parameters ) => { oninstruction ( opcode , parameters ) ; this . onTunnelInstruction . next ( { opcode , parameters } ) ; } ; } ) ( this . tunnel . oninstruction ) ; } param state private handleClientError ( status ) { this . disconnect ( ) ; this . setState ( RemoteDesktopManager . STATE . CLIENT_ERROR ) ; } param state private handleClientStateChange ( state ) { switch ( state ) { case 0 : this . setState ( RemoteDesktopManager . STATE . IDLE ) ; break ; case 1 : break ; case 2 : this . setState ( RemoteDesktopManager . STATE . WAITING ) ; break ; case 3 : this . setState ( RemoteDesktopManager . STATE . CONNECTED ) ; break ; case 4 : case 5 : break ; } } param status private handleTunnelError ( status ) { this . disconnect ( ) ; this . setState ( RemoteDesktopManager . STATE . TUNNEL_ERROR ) ; } param state private handleTunnelStateChange ( state ) { switch ( state ) { case 1 : this . setState ( RemoteDesktopManager . STATE . CONNECTING ) ; break ; case 2 : this . setState ( RemoteDesktopManager . STATE . DISCONNECTED ) ; break ; } } }