import { AfterViewInit , ChangeDetectionStrategy , Component , DoCheck , ElementRef , EventEmitter , forwardRef , Input , KeyValueDiffer , KeyValueDiffers , NgZone , OnDestroy , Output , ViewChild , } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { Editor , EditorChangeLinkedList , EditorFromTextArea , ScrollInfo , fromTextArea } from 'codemirror' ; function normalizeLineEndings ( str ) { if ( ! str ) { return str ; } return str . replace ( /\r\n|\r/g , '\n' ) ; } @ Component ( { selector : 'ratel-codemirror' , templateUrl : './codemirror.component.html' , styleUrls : [ './codemirror.component.scss' ] , providers : [ { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => CodemirrorComponent ) , multi : true , } , ] , preserveWhitespaces : false , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class CodemirrorComponent implements AfterViewInit , OnDestroy , ControlValueAccessor , DoCheck { @ Input ( ) className = '' ; @ Input ( ) name = 'codemirror' ; @ Input ( ) autoFocus = false ; /**
     * set options for codemirror
     */ @ Input ( ) set options ( value ) { this . _options = value ; if ( ! this . _differ && value ) { this . _differ = this . _differs . find ( value ) . create ( ) ; } } @ Input ( ) preserveScrollPosition = false ; @ Output ( ) cursorActivity = new EventEmitter < Editor > ( ) ; @ Output ( ) focusChange = new EventEmitter < boolean > ( ) ; @ Output ( ) scroll = new EventEmitter < ScrollInfo > ( ) ; @ ViewChild ( 'ref' , { static : false } ) ref : ElementRef ; value = '' ; disabled = false ; isFocused = false ; codeMirror : EditorFromTextArea ; private _differ : KeyValueDiffer < string , any > ; private _options : any ; constructor ( private _differs , private _ngZone ) { } ngAfterViewInit ( ) { if ( ! this . ref ) { return ; } this . _ngZone . runOutsideAngular ( ( ) => { this . codeMirror = fromTextArea ( this . ref . nativeElement , this . _options ) ; this . codeMirror . on ( 'cursorActivity' , cm => this . _ngZone . run ( ( ) => this . cursorActive ( cm ) ) ) ; this . codeMirror . on ( 'scroll' , this . scrollChanged . bind ( this ) ) ; this . codeMirror . on ( 'blur' , ( ) => this . _ngZone . run ( ( ) => this . focusChanged ( false ) ) ) ; this . codeMirror . on ( 'focus' , ( ) => this . _ngZone . run ( ( ) => this . focusChanged ( true ) ) ) ; this . codeMirror . on ( 'change' , ( cm , change ) => this . _ngZone . run ( ( ) => this . codemirrorValueChanged ( cm , change ) ) ) ; this . codeMirror . setValue ( this . value ) ; } ) ; } ngDoCheck ( ) { if ( ! this . _differ ) { return ; } const changes = this . _differ . diff ( this . _options ) ; if ( changes ) { changes . forEachChangedItem ( option => this . setOptionIfChanged ( option . key , option . currentValue ) ) ; changes . forEachAddedItem ( option => this . setOptionIfChanged ( option . key , option . currentValue ) ) ; changes . forEachRemovedItem ( option => this . setOptionIfChanged ( option . key , option . currentValue ) ) ; } } ngOnDestroy ( ) { if ( this . codeMirror ) { this . codeMirror . toTextArea ( ) ; } } codemirrorValueChanged ( cm , change ) { if ( change . origin !== 'setValue' ) { this . value = cm . getValue ( ) ; this . onChange ( this . value ) ; } } setOptionIfChanged ( optionName , newValue ) { if ( ! this . codeMirror ) { return ; } this . codeMirror . setOption ( optionName , newValue ) ; } focusChanged ( focused ) { this . onTouched ( ) ; this . isFocused = focused ; this . focusChange . emit ( focused ) ; } scrollChanged ( cm ) { this . scroll . emit ( cm . getScrollInfo ( ) ) ; } cursorActive ( cm ) { this . cursorActivity . emit ( cm ) ; } /** Implemented as part of ControlValueAccessor. */ writeValue ( value ) { if ( value === null || value === undefined ) { return ; } if ( ! this . codeMirror ) { this . value = value ; return ; } const cur = this . codeMirror . getValue ( ) ; if ( value !== cur && normalizeLineEndings ( cur ) !== normalizeLineEndings ( value ) ) { this . value = value ; if ( this . preserveScrollPosition ) { const prevScrollPosition = this . codeMirror . getScrollInfo ( ) ; this . codeMirror . setValue ( this . value ) ; this . codeMirror . scrollTo ( prevScrollPosition . left , prevScrollPosition . top ) ; } else { this . codeMirror . setValue ( this . value ) ; } } } /** Implemented as part of ControlValueAccessor. */ registerOnChange ( fn ) { this . onChange = fn ; } /** Implemented as part of ControlValueAccessor. */ registerOnTouched ( fn ) { this . onTouched = fn ; } /** Implemented as part of ControlValueAccessor. */ setDisabledState ( isDisabled ) { this . disabled = isDisabled ; this . setOptionIfChanged ( 'readOnly' , this . disabled ) ; } /** Implemented as part of ControlValueAccessor. */ private onChange = ( _ ) => { } ; /** Implemented as part of ControlValueAccessor. */ private onTouched = ( ) => { } ; }