@@ -3,14 +3,19 @@ import '@github/text-expander-element';
33import  $  from  'jquery' ; 
44import  { attachTribute }  from  '../tribute.ts' ; 
55import  { hideElem ,  showElem ,  autosize ,  isElemVisible }  from  '../../utils/dom.ts' ; 
6- import  { initEasyMDEPaste ,  initTextareaEvents }  from  './EditorUpload.ts' ; 
6+ import  { 
7+   EventUploadStateChanged , 
8+   initEasyMDEPaste , 
9+   initTextareaEvents , 
10+   triggerUploadStateChanged , 
11+ }  from  './EditorUpload.ts' ; 
712import  { handleGlobalEnterQuickSubmit }  from  './QuickSubmit.ts' ; 
813import  { renderPreviewPanelContent }  from  '../repo-editor.ts' ; 
914import  { easyMDEToolbarActions }  from  './EasyMDEToolbarActions.ts' ; 
1015import  { initTextExpander }  from  './TextExpander.ts' ; 
1116import  { showErrorToast }  from  '../../modules/toast.ts' ; 
1217import  { POST }  from  '../../modules/fetch.ts' ; 
13- import  { initTextareaMarkdown }  from  './EditorMarkdown.ts' ; 
18+ import  { EventEditorContentChanged ,   initTextareaMarkdown ,   triggerEditorContentChanged }  from  './EditorMarkdown.ts' ; 
1419import  { DropzoneCustomEventReloadFiles ,  initDropzone }  from  '../dropzone.ts' ; 
1520
1621let  elementIdCounter  =  0 ; 
@@ -37,7 +42,34 @@ export function validateTextareaNonEmpty(textarea) {
3742  return  true ; 
3843} 
3944
40- class  ComboMarkdownEditor  { 
45+ export  class  ComboMarkdownEditor  { 
46+   static  EventEditorContentChanged  =  EventEditorContentChanged ; 
47+   static  EventUploadStateChanged  =  EventUploadStateChanged ; 
48+ 
49+   public  container  : HTMLElement ; 
50+ 
51+   // TODO: use correct types to replace these "any" types 
52+   options : any ; 
53+ 
54+   tabEditor : HTMLElement ; 
55+   tabPreviewer : HTMLElement ; 
56+ 
57+   easyMDE : any ; 
58+   easyMDEToolbarActions : any ; 
59+   easyMDEToolbarDefault : any ; 
60+ 
61+   textarea : HTMLTextAreaElement  &  { _giteaComboMarkdownEditor : any } ; 
62+   textareaMarkdownToolbar : HTMLElement ; 
63+   textareaAutosize : any ; 
64+ 
65+   dropzone : HTMLElement ; 
66+   attachedDropzoneInst : any ; 
67+ 
68+   previewUrl : string ; 
69+   previewContext : string ; 
70+   previewMode : string ; 
71+   previewWiki : boolean ; 
72+ 
4173  constructor ( container ,  options  =  { } )  { 
4274    container . _giteaComboMarkdownEditor  =  this ; 
4375    this . options  =  options ; 
@@ -63,14 +95,13 @@ class ComboMarkdownEditor {
6395
6496  setupContainer ( )  { 
6597    initTextExpander ( this . container . querySelector ( 'text-expander' ) ) ; 
66-     this . container . addEventListener ( 'ce-editor-content-changed' ,  ( e )  =>  this . options ?. onContentChanged ?.( this ,  e ) ) ; 
6798  } 
6899
69100  setupTextarea ( )  { 
70101    this . textarea  =  this . container . querySelector ( '.markdown-text-editor' ) ; 
71102    this . textarea . _giteaComboMarkdownEditor  =  this ; 
72103    this . textarea . id  =  `_combo_markdown_editor_${ String ( elementIdCounter ++ ) }  ; 
73-     this . textarea . addEventListener ( 'input' ,  ( e )  =>  this . options ?. onContentChanged ?. ( this ,   e ) ) ; 
104+     this . textarea . addEventListener ( 'input' ,  ( )  =>  triggerEditorContentChanged ( this . container ) ) ; 
74105    this . applyEditorHeights ( this . textarea ,  this . options . editorHeights ) ; 
75106
76107    if  ( this . textarea . getAttribute ( 'data-disable-autosize' )  !==  'true' )  { 
@@ -115,15 +146,21 @@ class ComboMarkdownEditor {
115146
116147  async  setupDropzone ( )  { 
117148    const  dropzoneParentContainer  =  this . container . getAttribute ( 'data-dropzone-parent-container' ) ; 
118-     if  ( dropzoneParentContainer )  { 
119-       this . dropzone  =  this . container . closest ( this . container . getAttribute ( 'data-dropzone-parent-container' ) ) ?. querySelector ( '.dropzone' ) ; 
120-       if  ( this . dropzone )  this . attachedDropzoneInst  =  await  initDropzone ( this . dropzone ) ; 
121-     } 
149+     if  ( ! dropzoneParentContainer )  return ; 
150+     this . dropzone  =  this . container . closest ( this . container . getAttribute ( 'data-dropzone-parent-container' ) ) ?. querySelector ( '.dropzone' ) ; 
151+     if  ( ! this . dropzone )  return ; 
152+ 
153+     this . attachedDropzoneInst  =  await  initDropzone ( this . dropzone ) ; 
154+     // dropzone events 
155+     // * "processing" means a file is being uploaded 
156+     // * "queuecomplete" means all files have been uploaded 
157+     this . attachedDropzoneInst . on ( 'processing' ,  ( )  =>  triggerUploadStateChanged ( this . container ) ) ; 
158+     this . attachedDropzoneInst . on ( 'queuecomplete' ,  ( )  =>  triggerUploadStateChanged ( this . container ) ) ; 
122159  } 
123160
124161  dropzoneGetFiles ( )  { 
125162    if  ( ! this . dropzone )  return  null ; 
126-     return  Array . from ( this . dropzone . querySelectorAll ( '.files [name=files]' ) ,  ( el )  =>  el . value ) ; 
163+     return  Array . from ( this . dropzone . querySelectorAll < HTMLInputElement > ( '.files [name=files]' ) ,  ( el )  =>  el . value ) ; 
127164  } 
128165
129166  dropzoneReloadFiles ( )  { 
@@ -137,8 +174,13 @@ class ComboMarkdownEditor {
137174    this . attachedDropzoneInst . emit ( DropzoneCustomEventReloadFiles ) ; 
138175  } 
139176
177+   isUploading ( )  { 
178+     if  ( ! this . dropzone )  return  false ; 
179+     return  this . attachedDropzoneInst . getQueuedFiles ( ) . length  ||  this . attachedDropzoneInst . getUploadingFiles ( ) . length ; 
180+   } 
181+ 
140182  setupTab ( )  { 
141-     const  tabs  =  this . container . querySelectorAll ( '.tabular.menu > .item' ) ; 
183+     const  tabs  =  this . container . querySelectorAll < HTMLElement > ( '.tabular.menu > .item' ) ; 
142184
143185    // Fomantic Tab requires the "data-tab" to be globally unique. 
144186    // So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic. 
@@ -170,7 +212,7 @@ class ComboMarkdownEditor {
170212      formData . append ( 'mode' ,  this . previewMode ) ; 
171213      formData . append ( 'context' ,  this . previewContext ) ; 
172214      formData . append ( 'text' ,  this . value ( ) ) ; 
173-       formData . append ( 'wiki' ,  this . previewWiki ) ; 
215+       formData . append ( 'wiki' ,  String ( this . previewWiki ) ) ; 
174216      const  response  =  await  POST ( this . previewUrl ,  { data : formData } ) ; 
175217      const  data  =  await  response . text ( ) ; 
176218      renderPreviewPanelContent ( $ ( panelPreviewer ) ,  data ) ; 
@@ -237,24 +279,24 @@ class ComboMarkdownEditor {
237279    easyMDEOpt . toolbar  =  this . parseEasyMDEToolbar ( EasyMDE ,  easyMDEOpt . toolbar  ??  this . easyMDEToolbarDefault ) ; 
238280
239281    this . easyMDE  =  new  EasyMDE ( easyMDEOpt ) ; 
240-     this . easyMDE . codemirror . on ( 'change' ,  ( ... args )  =>  { this . options ?. onContentChanged ?. ( this ,  ... args ) } ) ; 
282+     this . easyMDE . codemirror . on ( 'change' ,  ( )  =>  triggerEditorContentChanged ( this . container ) ) ; 
241283    this . easyMDE . codemirror . setOption ( 'extraKeys' ,  { 
242284      'Cmd-Enter' : ( cm )  =>  handleGlobalEnterQuickSubmit ( cm . getTextArea ( ) ) , 
243285      'Ctrl-Enter' : ( cm )  =>  handleGlobalEnterQuickSubmit ( cm . getTextArea ( ) ) , 
244286      Enter : ( cm )  =>  { 
245-         const  tributeContainer  =  document . querySelector ( '.tribute-container' ) ; 
287+         const  tributeContainer  =  document . querySelector < HTMLElement > ( '.tribute-container' ) ; 
246288        if  ( ! tributeContainer  ||  tributeContainer . style . display  ===  'none' )  { 
247289          cm . execCommand ( 'newlineAndIndent' ) ; 
248290        } 
249291      } , 
250292      Up : ( cm )  =>  { 
251-         const  tributeContainer  =  document . querySelector ( '.tribute-container' ) ; 
293+         const  tributeContainer  =  document . querySelector < HTMLElement > ( '.tribute-container' ) ; 
252294        if  ( ! tributeContainer  ||  tributeContainer . style . display  ===  'none' )  { 
253295          return  cm . execCommand ( 'goLineUp' ) ; 
254296        } 
255297      } , 
256298      Down : ( cm )  =>  { 
257-         const  tributeContainer  =  document . querySelector ( '.tribute-container' ) ; 
299+         const  tributeContainer  =  document . querySelector < HTMLElement > ( '.tribute-container' ) ; 
258300        if  ( ! tributeContainer  ||  tributeContainer . style . display  ===  'none' )  { 
259301          return  cm . execCommand ( 'goLineDown' ) ; 
260302        } 
@@ -314,13 +356,7 @@ export function getComboMarkdownEditor(el) {
314356  return  el ?. _giteaComboMarkdownEditor ; 
315357} 
316358
317- export  async  function  initComboMarkdownEditor ( container ,  options  =  { } )  { 
318-   if  ( container  instanceof  $ )  { 
319-     if  ( container . length  !==  1 )  { 
320-       throw  new  Error ( 'initComboMarkdownEditor: container must be a single element' ) ; 
321-     } 
322-     container  =  container [ 0 ] ; 
323-   } 
359+ export  async  function  initComboMarkdownEditor ( container : HTMLElement ,  options  =  { } )  { 
324360  if  ( ! container )  { 
325361    throw  new  Error ( 'initComboMarkdownEditor: container is null' ) ; 
326362  } 
0 commit comments