@ -8,43 +8,46 @@ import {
generateMarkdownLinkForAttachment ,
} from '../dropzone.ts' ;
import type CodeMirror from 'codemirror' ;
import type EasyMDE from 'easymde' ;
import type { DropzoneFile } from 'dropzone' ;
let uploadIdCounter = 0 ;
export const EventUploadStateChanged = 'ce-upload-state-changed' ;
export function triggerUploadStateChanged ( target ) {
export function triggerUploadStateChanged ( target : HTMLElement ) {
target . dispatchEvent ( new CustomEvent ( EventUploadStateChanged , { bubbles : true } ) ) ;
}
function uploadFile ( dropzoneEl , file ) {
function uploadFile ( dropzoneEl : HTMLElement , file : F ile ) {
return new Promise ( ( resolve ) = > {
const curUploadId = uploadIdCounter ++ ;
file . _giteaUploadId = curUploadId ;
( file as any ) . _giteaUploadId = curUploadId ;
const dropzoneInst = dropzoneEl . dropzone ;
const onUploadDone = ( { file } ) = > {
const onUploadDone = ( { file } : { file : any } ) = > {
if ( file . _giteaUploadId === curUploadId ) {
dropzoneInst . off ( DropzoneCustomEventUploadDone , onUploadDone ) ;
resolve ( file ) ;
}
} ;
dropzoneInst . on ( DropzoneCustomEventUploadDone , onUploadDone ) ;
dropzoneInst . handleFiles ( [ file ] ) ;
// FIXME: this is not entirely correct because `file` does not satisfy DropzoneFile (we have abused the Dropzone for long time)
dropzoneInst . addFile ( file as DropzoneFile ) ;
} ) ;
}
class TextareaEditor {
editor : HTMLTextAreaElement ;
editor : HTMLTextAreaElement ;
constructor ( editor ) {
constructor ( editor : HTMLTextAreaElement ) {
this . editor = editor ;
}
insertPlaceholder ( value ) {
insertPlaceholder ( value : string ) {
textareaInsertText ( this . editor , value ) ;
}
replacePlaceholder ( oldVal , newVal ) {
replacePlaceholder ( oldVal : string , newVal : string ) {
const editor = this . editor ;
const startPos = editor . selectionStart ;
const endPos = editor . selectionEnd ;
@ -65,11 +68,11 @@ class TextareaEditor {
class CodeMirrorEditor {
editor : CodeMirror.EditorFromTextArea ;
constructor ( editor ) {
constructor ( editor : CodeMirror.EditorFromTextArea ) {
this . editor = editor ;
}
insertPlaceholder ( value ) {
insertPlaceholder ( value : string ) {
const editor = this . editor ;
const startPoint = editor . getCursor ( 'start' ) ;
const endPoint = editor . getCursor ( 'end' ) ;
@ -80,7 +83,7 @@ class CodeMirrorEditor {
triggerEditorContentChanged ( editor . getTextArea ( ) ) ;
}
replacePlaceholder ( oldVal , newVal ) {
replacePlaceholder ( oldVal : string , newVal : string ) {
const editor = this . editor ;
const endPoint = editor . getCursor ( 'end' ) ;
if ( editor . getSelection ( ) === oldVal ) {
@ -96,7 +99,7 @@ class CodeMirrorEditor {
}
}
async function handleUploadFiles ( editor , dropzoneEl , files , e ) {
async function handleUploadFiles ( editor : CodeMirrorEditor | TextareaEditor , dropzoneEl : HTMLElement , files : Array < File > | FileList , e : Event ) {
e . preventDefault ( ) ;
for ( const file of files ) {
const name = file . name . slice ( 0 , file . name . lastIndexOf ( '.' ) ) ;
@ -109,13 +112,13 @@ async function handleUploadFiles(editor, dropzoneEl, files, e) {
}
}
export function removeAttachmentLinksFromMarkdown ( text , fileUuid ) {
export function removeAttachmentLinksFromMarkdown ( text : string , fileUuid : string ) {
text = text . replace ( new RegExp ( ` !? \\ [([^ \\ ]]+) \\ ] \\ (/?attachments/ ${ fileUuid } \\ ) ` , 'g' ) , '' ) ;
text = text . replace ( new RegExp ( ` <img[^>]+src="/?attachments/ ${ fileUuid } "[^>]*> ` , 'g' ) , '' ) ;
return text ;
}
function handleClipboardText ( textarea , e , { text , isShiftDown } ) {
function handleClipboardText ( textarea : HTMLTextAreaElement , e : ClipboardEvent , text : string , isShiftDown : boolean ) {
// pasting with "shift" means "paste as original content" in most applications
if ( isShiftDown ) return ; // let the browser handle it
@ -131,7 +134,7 @@ function handleClipboardText(textarea, e, {text, isShiftDown}) {
}
// extract text and images from "paste" event
function getPastedContent ( e ) {
function getPastedContent ( e : ClipboardEvent ) {
const images = [ ] ;
for ( const item of e . clipboardData ? . items ? ? [ ] ) {
if ( item . type ? . startsWith ( 'image/' ) ) {
@ -142,8 +145,8 @@ function getPastedContent(e) {
return { text , images } ;
}
export function initEasyMDEPaste ( easyMDE , dropzoneEl ) {
const editor = new CodeMirrorEditor ( easyMDE . codemirror ) ;
export function initEasyMDEPaste ( easyMDE : EasyMDE , dropzoneEl : HTMLElement ) {
const editor = new CodeMirrorEditor ( easyMDE . codemirror as any ) ;
easyMDE . codemirror . on ( 'paste' , ( _ , e ) = > {
const { images } = getPastedContent ( e ) ;
if ( ! images . length ) return ;
@ -160,28 +163,28 @@ export function initEasyMDEPaste(easyMDE, dropzoneEl) {
} ) ;
}
export function initTextareaEvents ( textarea , dropzoneEl ) {
export function initTextareaEvents ( textarea : HTMLTextAreaElement , dropzoneEl : HTMLElement ) {
let isShiftDown = false ;
textarea . addEventListener ( 'keydown' , ( e ) = > {
textarea . addEventListener ( 'keydown' , ( e : KeyboardEvent ) = > {
if ( e . shiftKey ) isShiftDown = true ;
} ) ;
textarea . addEventListener ( 'keyup' , ( e ) = > {
textarea . addEventListener ( 'keyup' , ( e : KeyboardEvent ) = > {
if ( ! e . shiftKey ) isShiftDown = false ;
} ) ;
textarea . addEventListener ( 'paste' , ( e ) = > {
textarea . addEventListener ( 'paste' , ( e : ClipboardEvent ) = > {
const { images , text } = getPastedContent ( e ) ;
if ( images . length && dropzoneEl ) {
handleUploadFiles ( new TextareaEditor ( textarea ) , dropzoneEl , images , e ) ;
} else if ( text ) {
handleClipboardText ( textarea , e , { text , isShiftDown } ) ;
handleClipboardText ( textarea , e , text , isShiftDown ) ;
}
} ) ;
textarea . addEventListener ( 'drop' , ( e ) = > {
textarea . addEventListener ( 'drop' , ( e : DragEvent ) = > {
if ( ! e . dataTransfer . files . length ) return ;
if ( ! dropzoneEl ) return ;
handleUploadFiles ( new TextareaEditor ( textarea ) , dropzoneEl , e . dataTransfer . files , e ) ;
} ) ;
dropzoneEl ? . dropzone . on ( DropzoneCustomEventRemovedFile , ( { fileUuid } ) = > {
dropzoneEl ? . dropzone . on ( DropzoneCustomEventRemovedFile , ( { fileUuid } : { fileUuid : string } ) = > {
const newText = removeAttachmentLinksFromMarkdown ( textarea . value , fileUuid ) ;
if ( textarea . value !== newText ) textarea . value = newText ;
} ) ;