import { Component , Input , Output , ViewChild , EventEmitter , NgZone , forwardRef , QueryList , AfterViewInit , ContentChildren , SimpleChanges , OnChanges , OnDestroy , ElementRef } from '@angular/core' ; import { NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CKButtonDirective } from './ckbutton.directive' ; import { CKGroupDirective } from './ckgroup.directive' ; /**
 * CKEditor component
 * Usage :
 *  <ckeditor [(ngModel)]="data" [config]="{...}" debounce="500"></ckeditor>
 */ @ Component ( { selector : 'ckeditor' , providers : [ { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => CKEditorComponent ) , multi : true , } , ] , template : `<textarea #host></textarea>` , } ) export class CKEditorComponent implements OnChanges , AfterViewInit , OnDestroy { @ Input ( ) config : CKEDITOR . config ; @ Input ( ) readonly : boolean ; @ Input ( ) debounce : string ; @ Output ( ) change = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) editorChange = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) ready = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) blur = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) focus = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) contentDom = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) fileUploadRequest = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) fileUploadResponse = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) paste = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ Output ( ) drop = new EventEmitter < CKEDITOR . eventInfo > ( ) ; @ ViewChild ( 'host' , { static : false } ) host : ElementRef < HTMLTextAreaElement > ; @ ContentChildren ( CKButtonDirective ) toolbarButtons : QueryList < CKButtonDirective > ; @ ContentChildren ( CKGroupDirective ) toolbarGroups : QueryList < CKGroupDirective > ; _value = '' ; instance : CKEDITOR . editor ; debounceTimeout : number ; private destroyed = false ; /**
     * Constructor
     */ constructor ( private zone ) { } get value ( ) : string { return this . _value ; } @ Input ( ) set value ( v ) { if ( v !== this . _value ) { this . _value = v ; this . onChange ( v ) ; } } ngOnChanges ( changes ) { if ( changes . readonly && this . instance ) { this . instance . setReadOnly ( changes . readonly . currentValue ) ; } } /**
     * On component destroy
     */ ngOnDestroy ( ) { this . destroyed = true ; this . zone . runOutsideAngular ( ( ) => { if ( this . instance ) { CKEDITOR . removeAllListeners ( ) ; this . instance . destroy ( ) ; this . instance = null ; } } ) ; } /**
     * On component view init
     */ ngAfterViewInit ( ) { if ( this . destroyed ) { return ; } this . ckeditorInit ( this . config || { } ) ; } /**
     * On component view checked
     */ ngAfterViewChecked ( ) { this . ckeditorInit ( this . config || { } ) ; } /**
     * Value update process
     */ updateValue ( value ) { this . zone . run ( ( ) => { this . value = value ; this . onChange ( value ) ; this . onTouched ( ) ; this . change . emit ( value ) ; } ) ; } /**
     * CKEditor init
     */ ckeditorInit ( config ) { if ( typeof CKEDITOR === 'undefined' ) { console . warn ( 'CKEditor 4.x is missing (http://ckeditor.com/)' ) ; } else { if ( this . instance || ! this . documentContains ( this . host . nativeElement ) ) { return ; } if ( this . readonly ) { config . readOnly = this . readonly ; } this . instance = CKEDITOR . replace ( this . host . nativeElement , config ) ; this . instance . setData ( this . value ) ; this . instance . on ( 'instanceReady' , ( evt ) => { if ( this . instance . getData ( ) !== this . value ) { this . instance . setData ( this . value ) ; } this . ready . emit ( evt ) ; } ) ; this . instance . on ( 'change' , ( evt ) => { this . onTouched ( ) ; const value = this . instance . getData ( ) ; if ( this . value !== value ) { if ( this . debounce ) { if ( this . debounceTimeout ) { clearTimeout ( this . debounceTimeout ) ; } this . debounceTimeout = window . setTimeout ( ( ) => { this . updateValue ( value ) ; this . debounceTimeout = null ; } , parseInt ( this . debounce ) ) ; } else { this . updateValue ( value ) ; } } this . editorChange . emit ( evt ) ; } ) ; this . instance . on ( 'blur' , ( evt ) => { this . blur . emit ( evt ) ; } ) ; this . instance . on ( 'focus' , ( evt ) => { this . focus . emit ( evt ) ; } ) ; this . instance . on ( 'contentDom' , ( evt ) => { this . contentDom . emit ( evt ) ; } ) ; this . instance . on ( 'fileUploadRequest' , ( evt ) => { this . fileUploadRequest . emit ( evt ) ; } ) ; this . instance . on ( 'fileUploadResponse' , ( evt ) => { this . fileUploadResponse . emit ( evt ) ; } ) ; this . instance . on ( 'paste' , ( evt ) => { this . paste . emit ( evt ) ; } ) ; this . instance . on ( 'drop' , ( evt ) => { this . drop . emit ( evt ) ; } ) ; this . toolbarGroups . forEach ( ( group ) => { group . initialize ( this ) ; } ) ; this . toolbarButtons . forEach ( ( button ) => { button . initialize ( this ) ; } ) ; } } /**
     * Implements ControlValueAccessor
     */ writeValue ( value ) { this . _value = value ; if ( this . instance ) this . instance . setData ( value ) ; } onChange : ( _ : string ) => void ; onTouched : ( ) => void ; registerOnChange ( fn ) { this . onChange = fn ; } registerOnTouched ( fn ) { this . onTouched = fn ; } private documentContains ( node ) { return document . contains ? document . contains ( node ) : document . body . contains ( node ) ; } }