import { BehaviorSubject , noop , Observable , timer } from 'rxjs' ; import { debounceTime , map , tap , withLatestFrom } from 'rxjs/operators' ; import { ChangeDetectorRef , Component , ElementRef , HostListener , OnDestroy , OnInit , ViewChild } from '@angular/core' ; import { showError , showVisualization } from '@app/helpers' ; import { HasSubscriptionsDirective } from '@app/helpers/has-subscriptions' ; import { filterTruthy } from '@app/helpers/observables' ; import { Nbtutor } from '@app/models' ; import { CodeCell } from '@app/models/cell' ; import { VisualizationData } from '@app/models/visualization' ; import { CellDataService , CellService , CodeMirrorService , JsPlumbService , LoggerService } from '@app/services' ; @ Component ( { selector : 'nbtutor-canvas' , templateUrl : './canvas.component.html' , providers : [ CellDataService , CellService , JsPlumbService , CodeMirrorService ] , } ) export class CanvasComponent extends HasSubscriptionsDirective implements OnInit , OnDestroy , Nbtutor { protected _name = 'CanvasComponent' ; protected _dataPollTime = 5 * 60 * 1000 ; protected _changeDetectPollTime = 100 ; protected _canvasElementRefSubject$ = new BehaviorSubject < ElementRef < HTMLElement > > ( null ) ; protected _canvasElementRef$ = this . _canvasElementRefSubject$ . asObservable ( ) . pipe ( filterTruthy ( ) ) ; cellSvc = this . _cellSvc ; @ ViewChild ( 'canvas' , { static : false } ) set canvasElementRef ( elementRef ) { this . _loggerSvc . logDebug ( `${ this . _name } >> set canvasElement` , { elementRef } ) ; this . _canvasElementRefSubject$ . next ( elementRef ) ; } constructor ( protected _cellSvc , protected _cellDataSvc , protected _jsPlumbSvc , protected _codeMirrorSvc , protected _changeDetector , protected _loggerSvc ) { super ( ) ; } protected documentContains ( other ) { return document . contains ( other ) ; } protected getElement$ ( cell$ ) { return cell$ . pipe ( map ( ( cell ) => ( cell || { element : null } ) . element ) ) ; } protected getJsPlumbSetupEffect$ ( ) { return this . _canvasElementRef$ . pipe ( debounceTime ( 10 ) , tap ( ( elementRef ) => this . _jsPlumbSvc . setupJsPlumb ( elementRef . nativeElement ) ) ) ; } protected getIsOrphanedTimer$ ( cell$ ) { return timer ( this . _dataPollTime , this . _dataPollTime ) . pipe ( withLatestFrom ( this . getElement$ ( cell$ ) ) , map ( ( [ _ , element ] ) => ! ! element ? ! this . documentContains ( element [ 0 ] ) : true ) , tap ( ( isOrphaned ) => this . _loggerSvc . logDebug ( `${ this . _name } >> isOrphaned$` , { isOrphaned } ) ) , tap ( ( isOrphaned ) => ! ! isOrphaned ? this . ngOnDestroy ( ) : noop ) ) ; } protected getDetectChangesTimer$ ( ) { return timer ( this . _changeDetectPollTime , this . _changeDetectPollTime ) . pipe ( tap ( ( ) => this . _changeDetector . detectChanges ( ) ) ) ; } @ HostListener ( 'window:resize' ) onWindowResize ( ) { this . _jsPlumbSvc . repaintConnectors ( ) ; } ngOnInit ( ) { this . _loggerSvc . logDebug ( `${ this . _name } >> ngOnInit` ) ; const setupJsPlumb$ = this . getJsPlumbSetupEffect$ ( ) ; const isOrphaned$ = this . getIsOrphanedTimer$ ( this . _cellSvc . cell$ ) ; const detectChanges$ = this . getDetectChangesTimer$ ( ) ; this . _observables = [ setupJsPlumb$ , isOrphaned$ , detectChanges$ ] ; this . subscribeToObservables ( ) ; } showVisualization ( visualization ) { return showVisualization ( visualization ) ; } showError ( visualization ) { return showError ( visualization ) ; } ngOnDestroy ( ) { this . _loggerSvc . logDebug ( `${ this . _name } >> ngOnDestroy` ) ; this . _cellSvc . setCodeCell ( null ) ; this . _jsPlumbSvc . ngOnDestroy ( ) ; this . _codeMirrorSvc . ngOnDestroy ( ) ; this . _cellSvc . ngOnDestroy ( ) ; this . _cellDataSvc . ngOnDestroy ( ) ; super . ngOnDestroy ( ) ; } }