11import { Resolve , Obj , Fun , Global } from '@ephox/katamari' ;
2+ import { TinyMCE , Editor } from 'tinymce' ;
23import { ScriptLoader } from '../utils/ScriptLoader' ;
4+ type EditorOptions = Parameters < TinyMCE [ 'init' ] > [ 0 ] ;
5+ type EventHandler = Parameters < Editor [ 'on' ] > [ 1 ] ;
36
47// the advanced config will accept any attributes that start with `config-`
58// and try to parse them as JSON or resolve them on the Global state.
@@ -11,17 +14,18 @@ enum Status {
1114 Ready
1215}
1316
14- const parseJsonResolveGlobals = ( value : string ) : any => {
17+ const parseJsonResolveGlobals = ( value : string ) : unknown => {
1518 try {
1619 return JSON . parse ( value ) ;
1720 } catch ( _e ) { /* ignore */ }
1821 return Resolve . resolve ( value ) ;
1922} ;
20- const lookup = ( values : Record < string , any > ) => ( key : string ) => Obj . has ( values , key ) ? values [ key ] : key ;
23+ const isLookupKey = < T extends Record < string , any > , K extends keyof T > ( values : T , key : string | K ) : key is K => Obj . has ( values , key ) ;
24+ const lookup = < T extends Record < string , unknown > , K extends keyof T > ( values : T ) => ( key : string | K ) => isLookupKey ( values , key ) ? values [ key ] : key ;
2125
2226const parseGlobal = Resolve . resolve ;
2327const parseString = Fun . identity ;
24- const parseFalseOrString = lookup ( { 'false' : false } ) ;
28+ const parseFalseOrString = lookup ( { 'false' : false as const } ) ;
2529const parseBooleanOrString = lookup ( { 'true' : true , 'false' : false } ) ;
2630const parseNumberOrString = ( value : string ) => / ^ \d + $ / . test ( value ) ? Number . parseInt ( value , 10 ) : value ;
2731
@@ -59,9 +63,9 @@ const configRenames: Record<string, string> = {
5963class TinyMceEditor extends HTMLElement {
6064 private _status : Status ;
6165 private _shadowDom : ShadowRoot ;
62- private _editor : any ;
66+ private _editor : Editor | undefined ;
6367 private _form : HTMLFormElement | null ;
64- private _eventHandlers : Record < string , any > ;
68+ private _eventHandlers : Record < string , EventHandler | undefined > ;
6569 private _mutationObserver : MutationObserver ;
6670
6771 static get formAssociated ( ) {
@@ -105,17 +109,21 @@ class TinyMceEditor extends HTMLElement {
105109 } ) ;
106110 } ;
107111
108- private _formDataHandler = ( evt : Event ) => {
112+ private _formDataHandler = ( evt : FormDataEvent ) : void => {
109113 const name = this . name ;
110- if ( name !== null ) {
111- const data = ( evt as any ) . formData as FormData ;
112- data . append ( name , this . value ) ;
114+ if ( name != null ) {
115+ const value = this . value ;
116+ if ( value != null ) {
117+ const data = evt . formData ;
118+ data . append ( name , value ) ;
119+ }
113120 }
114121 } ;
115122
116- private _updateEventAttr ( attrKey : string , attrValue : string | null ) {
123+ private _updateEventAttr ( attrKey : string , attrValue : string | null ) : void {
117124 const event = attrKey . substring ( 'on-' . length ) . toLowerCase ( ) ;
118- const handler = attrValue !== null ? Resolve . resolve ( attrValue ) : undefined ;
125+ const resolved = attrValue !== null ? Resolve . resolve ( attrValue ) : undefined ;
126+ const handler = typeof resolved === 'function' ? resolved as EventHandler : undefined ;
119127 if ( this . _eventHandlers [ event ] !== handler ) {
120128 if ( this . _editor && this . _eventHandlers [ event ] ) {
121129 this . _editor . off ( event , this . _eventHandlers [ event ] ) ;
@@ -131,7 +139,7 @@ class TinyMceEditor extends HTMLElement {
131139 }
132140 }
133141
134- private _updateForm ( ) {
142+ private _updateForm ( ) : void {
135143 if ( this . isConnected ) {
136144 const formId = this . getAttribute ( 'form' ) ;
137145 const form = formId !== null ? this . ownerDocument . querySelector < HTMLFormElement > ( 'form#' + formId ) : this . closest ( 'form' ) ;
@@ -152,18 +160,18 @@ class TinyMceEditor extends HTMLElement {
152160 }
153161 }
154162
155- private _getTinymce ( ) {
163+ private _getTinymce ( ) : TinyMCE {
156164 return Global . tinymce ;
157165 }
158166
159- private _getConfig ( ) {
160- const config : Record < string , unknown > = parseGlobal ( this . getAttribute ( 'config' ) ?? '' ) ?? { } ;
167+ private _getConfig ( ) : EditorOptions {
168+ const config : EditorOptions = parseGlobal ( this . getAttribute ( 'config' ) ?? '' ) ?? { } ;
161169 for ( let i = 0 ; i < this . attributes . length ; i ++ ) {
162170 const attr = this . attributes . item ( i ) ;
163171 if ( attr !== null ) {
164172 if ( ADVANCED_CONFIG && attr . name . startsWith ( 'config-' ) ) {
165173 // add to config
166- const prop = attr . name . substr ( 'config-' . length ) ;
174+ const prop = attr . name . substring ( 'config-' . length ) ;
167175 config [ prop ] = parseJsonResolveGlobals ( attr . value ) ;
168176 } else if ( Obj . has ( configAttributes , attr . name ) ) {
169177 const prop = Obj . has ( configRenames , attr . name ) ? configRenames [ attr . name ] : attr . name ;
@@ -182,22 +190,24 @@ class TinyMceEditor extends HTMLElement {
182190 return config ;
183191 }
184192
185- private _getEventHandlers ( ) {
186- const handlers : Record < string , any > = { } ;
193+ private _getEventHandlers ( ) : Record < string , EventHandler > {
194+ const handlers : Record < string , EventHandler > = { } ;
187195 for ( let i = 0 ; i < this . attributes . length ; i ++ ) {
188196 const attr = this . attributes . item ( i ) ;
189197 if ( attr !== null ) {
190198 if ( attr . name . toLowerCase ( ) . startsWith ( 'on-' ) ) {
191- const event = attr . name . toLowerCase ( ) . substr ( 'on-' . length ) ;
199+ const event = attr . name . toLowerCase ( ) . substring ( 'on-' . length ) ;
192200 const handler = Resolve . resolve ( attr . value ) ;
193- handlers [ event ] = handler ;
201+ if ( typeof handler === 'function' ) {
202+ handlers [ event ] = handler as EventHandler ;
203+ }
194204 }
195205 }
196206 }
197207 return handlers ;
198208 }
199209
200- private _doInit ( ) {
210+ private _doInit ( ) : void {
201211 this . _status = Status . Initializing ;
202212 // load
203213 const target = document . createElement ( 'textarea' ) ;
@@ -207,10 +217,10 @@ class TinyMceEditor extends HTMLElement {
207217 }
208218 this . _shadowDom . appendChild ( target ) ;
209219 const baseConfig = this . _getConfig ( ) ;
210- const conf = {
220+ const conf : EditorOptions = {
211221 ...baseConfig ,
212222 target,
213- setup : ( editor : any ) => {
223+ setup : ( editor : Editor ) => {
214224 this . _editor = editor ;
215225 editor . on ( 'init' , ( _e : unknown ) => {
216226 this . _status = Status . Ready ;
@@ -219,8 +229,10 @@ class TinyMceEditor extends HTMLElement {
219229 // this assignment ensures the attribute is in sync with the editor
220230 this . readonly = this . readonly ;
221231 } ) ;
222- Object . keys ( this . _eventHandlers ) . forEach ( ( event ) => {
223- editor . on ( event , this . _eventHandlers [ event ] ) ;
232+ Obj . each ( this . _eventHandlers , ( handler , event ) => {
233+ if ( handler !== undefined ) {
234+ editor . on ( event , handler ) ;
235+ }
224236 } ) ;
225237 if ( typeof baseConfig . setup === 'function' ) {
226238 baseConfig . setup ( editor ) ;
@@ -231,18 +243,18 @@ class TinyMceEditor extends HTMLElement {
231243 this . _getTinymce ( ) . init ( conf ) ;
232244 }
233245
234- private _getTinymceSrc ( ) {
246+ private _getTinymceSrc ( ) : string {
235247 const src = this . getAttribute ( 'src' ) ;
236248 if ( src ) {
237249 return src ;
238250 }
239- const channel = this . getAttribute ( 'channel' ) ?? '5-stable ' ;
251+ const channel = this . getAttribute ( 'channel' ) ?? '6 ' ;
240252 const apiKey = this . hasAttribute ( 'api-key' ) ? this . getAttribute ( 'api-key' ) : 'no-api-key' ;
241253 return `https://cdn.tiny.cloud/1/${ apiKey } /tinymce/${ channel } /tinymce.min.js` ;
242254
243255 }
244256
245- private _loadTinyDoInit ( ) {
257+ private _loadTinyDoInit ( ) : void {
246258 if ( this . _getTinymce ( ) ) {
247259 this . _doInit ( ) ;
248260 } else {
@@ -254,7 +266,7 @@ class TinyMceEditor extends HTMLElement {
254266 }
255267 }
256268
257- attributeChangedCallback ( attribute : string , oldValue : string | null , newValue : string | null ) {
269+ attributeChangedCallback ( attribute : string , oldValue : string | null , newValue : string | null ) : void {
258270 if ( oldValue !== newValue ) {
259271 if ( attribute === 'form' ) {
260272 this . _updateForm ( ) ;
@@ -264,13 +276,13 @@ class TinyMceEditor extends HTMLElement {
264276 this . autofocus = newValue !== null ;
265277 } else if ( attribute === 'placeholder' ) {
266278 this . placeholder = newValue ;
267- } else if ( attribute . toLowerCase ( ) . startsWith ( 'on' ) ) {
279+ } else if ( attribute . toLowerCase ( ) . startsWith ( 'on- ' ) ) {
268280 this . _updateEventAttr ( attribute , newValue ) ;
269281 }
270282 }
271283 }
272284
273- connectedCallback ( ) {
285+ connectedCallback ( ) : void {
274286 this . _eventHandlers = this . _getEventHandlers ( ) ;
275287 this . _mutationObserver . observe ( this , { attributes : true , childList : false , subtree : false } ) ;
276288 this . _updateForm ( ) ;
@@ -279,22 +291,22 @@ class TinyMceEditor extends HTMLElement {
279291 }
280292 }
281293
282- disconnectedCallback ( ) {
294+ disconnectedCallback ( ) : void {
283295 this . _mutationObserver . disconnect ( ) ;
284296 this . _updateForm ( ) ;
285297 }
286298
287- get value ( ) {
288- return this . _status === Status . Ready ? this . _editor . getContent ( ) : undefined ;
299+ get value ( ) : string | null {
300+ return ( this . _status === Status . Ready ? this . _editor ? .getContent ( ) : undefined ) ?? null ;
289301 }
290302
291- set value ( newValue : string ) {
292- if ( this . _status === Status . Ready ) {
293- this . _editor . setContent ( newValue ) ;
303+ set value ( newValue : string | null ) {
304+ if ( this . _status === Status . Ready && newValue != null ) {
305+ this . _editor ? .setContent ( newValue ) ;
294306 }
295307 }
296308
297- get readonly ( ) {
309+ get readonly ( ) : boolean {
298310 if ( this . _editor ) {
299311 return this . _editor . mode . get ( ) === 'readonly' ;
300312 } else {
@@ -320,13 +332,13 @@ class TinyMceEditor extends HTMLElement {
320332 }
321333 }
322334
323- get placeholder ( ) {
335+ get placeholder ( ) : string | null {
324336 return this . getAttribute ( 'placeholder' ) ;
325337 }
326338
327339 set placeholder ( value : string | null ) {
328340 if ( this . _editor ) {
329- const target : HTMLTextAreaElement = this . _editor . getElement ( ) ;
341+ const target = this . _editor . getElement ( ) ;
330342 if ( target !== null ) {
331343 if ( value !== null ) {
332344 target . setAttribute ( 'placeholder' , value ) ;
@@ -346,7 +358,7 @@ class TinyMceEditor extends HTMLElement {
346358 }
347359 }
348360
349- get autofocus ( ) {
361+ get autofocus ( ) : boolean {
350362 return this . hasAttribute ( 'autofocus' ) ;
351363 }
352364
@@ -362,13 +374,13 @@ class TinyMceEditor extends HTMLElement {
362374 }
363375 }
364376
365- get form ( ) {
377+ get form ( ) : HTMLFormElement | null {
366378 return this . _form ;
367379 }
368- get name ( ) {
380+ get name ( ) : string | null {
369381 return this . getAttribute ( 'name' ) ;
370382 }
371- get type ( ) {
383+ get type ( ) : string {
372384 return this . localName ;
373385 }
374386}
0 commit comments