import { OnInit , OnDestroy , ComponentFactoryResolver , ComponentFactory , ComponentRef , ViewChild , ViewContainerRef , ElementRef , Input , } from '@angular/core' ; import { ArmObj } from '../../../../shared/models/arm/arm-obj' ; import { Site } from '../../../../shared/models/arm/site' ; import { Subscription } from 'rxjs/Subscription' ; import { ConsoleService } from './../services/console.service' ; import { KeyCodes , ConsoleConstants } from '../../../../shared/models/constants' ; import { ErrorComponent } from './error.component' ; import { MessageComponent } from './message.component' ; import { PromptComponent } from './prompt.component' ; import { Headers } from '@angular/http' ; import { PortalService } from '../../../../shared/services/portal.service' ; import { Subject } from 'rxjs/Subject' ; export abstract class AbstractConsoleComponent implements OnInit , OnDestroy { public resourceId : string ; public consoleType : number ; public isFocused = false ; public commandInParts = { leftCmd : '' , middleCmd : ' ' , rightCmd : '' } ; public dir : string ; public initialized = false ; public cleared = false ; protected enterPressed = false ; protected site : ArmObj < Site > ; protected siteSubscription : Subscription ; protected publishingCredSubscription : Subscription ; /*** Variables for Tab-key ***/ protected listOfDir : string [ ] = [ ] ; protected dirIndex = - 1 ; protected lastAPICall : Subscription = undefined ; protected tabKeyPointer : number ; /*** Variables for Command + Dir @Input ***/ protected command = '' ; protected ptrPosition = 0 ; protected commandHistory : string [ ] = [ '' ] ; protected commandHistoryIndex = 1 ; protected currentPrompt : ComponentRef < any > = null ; private _lastKeyPressed = - 1 ; private _promptComponent : ComponentFactory < any > ; private _messageComponent : ComponentFactory < any > ; private _errorComponent : ComponentFactory < any > ; private _msgComponents : ComponentRef < any > [ ] = [ ] ; private _ngUnsubscribe = new Subject ( ) ; private _armToken : string ; @ Input ( ) public appName : string ; /**
     * UI Elements
     */ @ ViewChild ( 'prompt' , { read : ViewContainerRef } ) private _prompt : ViewContainerRef ; @ ViewChild ( 'consoleText' ) private _consoleText : ElementRef ; constructor ( private _componentFactoryResolver , private _consoleService , private _portalService ) { } ngOnInit ( ) { this . _portalService . getStartupInfo ( ) . takeUntil ( this . _ngUnsubscribe ) . subscribe ( info => { this . _armToken = info . token ; if ( ! this . initialized ) { this . initializeConsole ( ) ; this . initialized = true ; this . addPromptComponent ( ) ; this . focusConsole ( ) ; } } ) ; this . _consoleService . getResourceId ( ) . takeUntil ( this . _ngUnsubscribe ) . subscribe ( resourceId => { this . resourceId = resourceId ; } ) ; } ngOnDestroy ( ) { this . _ngUnsubscribe . next ( ) ; this . siteSubscription . unsubscribe ( ) ; if ( this . lastAPICall && ! this . lastAPICall . closed ) { this . lastAPICall . unsubscribe ( ) ; } } /**
     * Intialize console specific variables like dir
     */ protected abstract initializeConsole ( ) ; /**
     * Mouse Press outside the console,
     * i.e. the console no longer in focus
     */ unFocusConsole ( ) { this . isFocused = false ; this . _renderPromptVariables ( ) ; } /**
     * Unfocus console manually
     */ unFocusConsoleManually ( ) { this . _consoleText . nativeElement . blur ( ) ; } /**
     * Console brought to focus when textarea comes into focus
     */ focusConsoleOnTabPress ( ) { this . isFocused = true ; this . _renderPromptVariables ( ) ; } /**
     * Mouse press inside the console, i.e. console comes into focus.
     * If already in focus, the console remains to be in focus.
     */ focusConsole ( ) { this . isFocused = true ; this . _renderPromptVariables ( ) ; this . _consoleText . nativeElement . focus ( ) ; } param event handlePaste ( event ) { const text = event . clipboardData . getData ( 'text/plain' ) ; this . command += text ; this . ptrPosition = this . command . length ; this . divideCommandForPtr ( ) ; } param event handleRightMouseClick ( event ) { return false ; } param event handleCopy ( event ) { if ( ! this . lastAPICall || this . lastAPICall . closed ) { this . _removePrompt ( ) ; this . addMessageComponent ( ) ; } else if ( ! this . lastAPICall . closed ) { this . lastAPICall . unsubscribe ( ) ; } this . _resetCommand ( ) ; this . addPromptComponent ( ) ; } param event keyEvent ( event ) { if ( ! this . _isKeyEventValid ( event . which ) ) { return ; } /**
         * Switch case on the key number
         */ switch ( event . which ) { case KeyCodes . backspace : { this . _backspaceKeyEvent ( ) ; break ; } case KeyCodes . tab : { event . preventDefault ( ) ; this . tabKeyEvent ( ) ; return ; } case KeyCodes . enter : { this . _enterKeyEvent ( ) ; break ; } case KeyCodes . escape : { this . _resetCommand ( ) ; break ; } case KeyCodes . space : { this . _appendToCommand ( ConsoleConstants . whitespace ) ; break ; } case KeyCodes . arrowLeft : { this . _leftArrowKeyEvent ( ) ; break ; } case KeyCodes . arrowUp : { this . _topArrowKeyEvent ( ) ; break ; } case KeyCodes . arrowRight : { this . _rightArrowKeyEvent ( ) ; break ; } case KeyCodes . arrowDown : { this . _downArrowKeyEvent ( ) ; break ; } default : { this . _appendToCommand ( event . key , event . which ) ; break ; } } this . _lastKeyPressed = event . which ; this . _renderPromptVariables ( ) ; this . _refreshTabFunctionElements ( ) ; } /**
     * Get the delimeter according to the app type
     */ protected abstract getMessageDelimeter ( ) ; /**
     * Get the command to list all the directories for the specific console-type
     */ protected abstract getTabKeyCommand ( ) ; /**
     * perform action on key pressed.
     */ protected abstract performAction ( cmd ? ) ; /**
     * Handle the tab-pressed event
     */ protected abstract tabKeyEvent ( ) ; /**
     * Get Kudu API URL
     */ protected abstract getKuduUri ( ) ; /**
     * Get the left-hand-side text for the console
     */ protected abstract getConsoleLeft ( ) ; /**
     * Connect to the kudu API and display the response;
     * both incase of an error or a valid response
     */ protected abstract connectToKudu ( ) ; /**
     * Get the header to connect to the KUDU API.
     */ protected getHeader ( ) { const headers = new Headers ( ) ; headers . append ( 'Content-Type' , 'application/json' ) ; headers . append ( 'Accept' , 'application/json' ) ; headers . append ( 'Authorization' , `Bearer ${ this . _armToken }` ) ; return headers ; } /**
     * Remove all the message history
     */ protected removeMsgComponents ( ) { let len = this . _msgComponents . length ; while ( len > 0 ) { -- len ; this . _msgComponents . pop ( ) . destroy ( ) ; } } param message protected addMessageComponent ( message ? ) { if ( ! this . _messageComponent ) { this . _messageComponent = this . _componentFactoryResolver . resolveComponentFactory ( MessageComponent ) ; } const msgComponent = this . _prompt . createComponent ( this . _messageComponent ) ; msgComponent . instance . isCommand = message ? false : true ; msgComponent . instance . loading = message ? false : true ; msgComponent . instance . message = message ? message : this . getConsoleLeft ( ) + this . command ; this . _msgComponents . push ( msgComponent ) ; this . _updateConsoleScroll ( ) ; } /**
     * Creates a new prompt box,
     * created everytime a command is entered by the user and
     * some response is generated from the server, or 'cls', 'exit'
     */ protected addPromptComponent ( ) { if ( ! this . _promptComponent ) { this . _promptComponent = this . _componentFactoryResolver . resolveComponentFactory ( PromptComponent ) ; } this . currentPrompt = this . _prompt . createComponent ( this . _promptComponent ) ; this . currentPrompt . instance . dir = this . getConsoleLeft ( ) ; this . currentPrompt . instance . consoleType = this . consoleType ; if ( this . _msgComponents . length > 0 ) { this . _msgComponents [ this . _msgComponents . length - 1 ] . instance . loading = false ; } if ( this . _msgComponents . length > 1 ) { this . _msgComponents [ this . _msgComponents . length - 2 ] . instance . loading = false ; } this . _updateConsoleScroll ( ) ; } param error protected addErrorComponent ( error ) { if ( ! this . _errorComponent ) { this . _errorComponent = this . _componentFactoryResolver . resolveComponentFactory ( ErrorComponent ) ; } const errorComponent = this . _prompt . createComponent ( this . _errorComponent ) ; this . _msgComponents . push ( errorComponent ) ; errorComponent . instance . message = error ; this . _updateConsoleScroll ( ) ; } /**
     * Divide the commands into left, current and right
     */ protected divideCommandForPtr ( ) { if ( this . ptrPosition < 0 || this . ptrPosition > this . command . length ) { return ; } if ( this . ptrPosition === this . command . length ) { this . commandInParts . leftCmd = this . command ; this . commandInParts . middleCmd = ConsoleConstants . whitespace ; this . commandInParts . rightCmd = '' ; return ; } this . commandInParts . leftCmd = this . command . substring ( 0 , this . ptrPosition ) ; this . commandInParts . middleCmd = this . command . substring ( this . ptrPosition , this . ptrPosition + 1 ) ; this . commandInParts . rightCmd = this . command . substring ( this . ptrPosition + 1 , this . command . length ) ; } /**
     * Change the command on tab-key event
     */ protected setCommandOnTabKeyEvent ( ) { this . dirIndex = ( this . dirIndex + 1 ) % this . listOfDir . length ; this . command = this . command . substring ( 0 , this . tabKeyPointer + 1 ) ; this . command += this . listOfDir [ this . dirIndex ] . trim ( ) ; } /**
     * Replace a part of the command with the file-name in the current directory
     */ protected replaceWithFileName ( ) { this . setCommandOnTabKeyEvent ( ) ; this . ptrPosition = this . command . length ; this . divideCommandForPtr ( ) ; } /**
     * Scroll to the latest test in the console
     */ private _updateConsoleScroll ( ) { window . setTimeout ( ( ) => { const el = document . getElementById ( 'console-body' ) ; if ( el ) { el . scrollTop = el . scrollHeight ; } } ) ; } private _isKeyEventValid ( key ) { if ( key === KeyCodes . unknown ) { return false ; } if ( this . enterPressed && key !== KeyCodes . ctrl && key !== KeyCodes . c ) { return false ; } return true ; } /**
     * Left Arrow key pressed
     */ private _leftArrowKeyEvent ( ) { if ( this . ptrPosition >= 1 ) { -- this . ptrPosition ; this . divideCommandForPtr ( ) ; } } /**
     * Right Arrow key pressed
     */ private _rightArrowKeyEvent ( ) { if ( this . ptrPosition < this . command . length ) { ++ this . ptrPosition ; this . divideCommandForPtr ( ) ; } } /**
     * Down Arrow key pressed
     */ private _downArrowKeyEvent ( ) { if ( this . commandHistory . length > 0 && this . commandHistoryIndex < this . commandHistory . length - 1 ) { this . commandHistoryIndex = ( this . commandHistoryIndex + 1 ) % this . commandHistory . length ; this . command = this . commandHistory [ this . commandHistoryIndex ] ; this . ptrPosition = this . command . length ; this . divideCommandForPtr ( ) ; } } /**
     * Top Arrow key pressed
     */ private _topArrowKeyEvent ( ) { if ( this . commandHistoryIndex > 0 ) { this . command = this . commandHistory [ this . commandHistoryIndex - 1 ] ; this . commandHistoryIndex = this . commandHistoryIndex === 1 ? 0 : -- this . commandHistoryIndex ; this . ptrPosition = this . command . length ; this . divideCommandForPtr ( ) ; } } /**
     * Backspace pressed by the user
     */ private _backspaceKeyEvent ( ) { if ( this . ptrPosition < 1 ) { return ; } this . commandInParts . leftCmd = this . commandInParts . leftCmd . slice ( 0 , - 1 ) ; if ( this . ptrPosition === this . command . length ) { this . command = this . commandInParts . leftCmd ; -- this . ptrPosition ; return ; } this . command = this . commandInParts . leftCmd + this . commandInParts . middleCmd + this . commandInParts . rightCmd ; -- this . ptrPosition ; this . divideCommandForPtr ( ) ; } /**
     * Handle the Enter key pressed operation
     */ private _enterKeyEvent ( ) { this . enterPressed = true ; const flag = this . performAction ( ) ; this . _removePrompt ( ) ; this . commandHistory . push ( this . command ) ; this . commandHistoryIndex = this . commandHistory . length ; if ( flag ) { this . addMessageComponent ( ) ; this . connectToKudu ( ) ; } else { this . addPromptComponent ( ) ; this . enterPressed = false ; } this . _resetCommand ( ) ; } /**
     * Remove the current prompt from the console
     */ private _removePrompt ( ) { const oldPrompt = document . getElementById ( 'prompt' ) ; if ( oldPrompt ) { oldPrompt . parentNode . removeChild ( oldPrompt ) ; } } /**
     * Refresh the tab elements,
     * i.e. the list of files/folder and the current dir index
     */ private _refreshTabFunctionElements ( ) { this . listOfDir . length = 0 ; this . dirIndex = - 1 ; } /**
     * Reset the command
     */ private _resetCommand ( ) { this . command = '' ; this . commandInParts . rightCmd = '' ; this . commandInParts . leftCmd = '' ; this . commandInParts . middleCmd = ConsoleConstants . whitespace ; this . ptrPosition = 0 ; } param cmd private _appendToCommand ( cmd , key ? ) { if ( key && ( ( key > KeyCodes . backspace && key <= KeyCodes . delete ) || ( key >= KeyCodes . leftWindow && key <= KeyCodes . select ) || ( key >= KeyCodes . f1 && key < KeyCodes . scrollLock ) ) ) { return ; } if ( key && ( key === KeyCodes . c || key === KeyCodes . v ) && this . _lastKeyPressed === KeyCodes . ctrl ) { return ; } this . commandHistoryIndex = this . commandHistory . length ; if ( this . ptrPosition === this . command . length ) { this . commandInParts . leftCmd += cmd ; this . command = this . commandInParts . leftCmd ; ++ this . ptrPosition ; return ; } this . commandInParts . leftCmd += cmd ; this . command = this . commandInParts . leftCmd + this . commandInParts . middleCmd + this . commandInParts . rightCmd ; ++ this . ptrPosition ; } /**
     * Render the dynamically loaded prompt box,
     * i.e. pass in the updated command the inFocus value to the PromptComponent.
     */ private _renderPromptVariables ( ) { if ( this . currentPrompt ) { this . currentPrompt . instance . command = this . command ; this . currentPrompt . instance . commandInParts = this . commandInParts ; this . currentPrompt . instance . isFocused = this . isFocused ; } } }