@ -1,11 +1,8 @@
import $ from 'jquery' ;
import $ from 'jquery' ;
import { createCommentEasyMDE , getAttachedEasyMDE } from './comp/EasyMDE.js' ;
import { initCompMarkupContentPreviewTab } from './comp/MarkupContentPreview.js' ;
import { initEasyMDEImagePaste } from './comp/ImagePaste.js' ;
import {
import {
initRepoIssueBranchSelect , initRepoIssueCodeCommentCancel , initRepoIssueCommentDelete ,
initRepoIssueBranchSelect , initRepoIssueCodeCommentCancel , initRepoIssueCommentDelete ,
initRepoIssueComments , initRepoIssueDependencyDelete , initRepoIssueReferenceIssue ,
initRepoIssueComments , initRepoIssueDependencyDelete , initRepoIssueReferenceIssue ,
initRepoIssueStatusButton , initRepoIssue TitleEdit , initRepoIssueWipToggle ,
initRepoIssueTitleEdit , initRepoIssueWipToggle ,
initRepoPullRequestUpdate , updateIssuesMeta , handleReply
initRepoPullRequestUpdate , updateIssuesMeta , handleReply
} from './repo-issue.js' ;
} from './repo-issue.js' ;
import { initUnicodeEscapeButton } from './repo-unicode-escape.js' ;
import { initUnicodeEscapeButton } from './repo-unicode-escape.js' ;
@ -19,27 +16,27 @@ import {
import { initCitationFileCopyContent } from './citation.js' ;
import { initCitationFileCopyContent } from './citation.js' ;
import { initCompLabelEdit } from './comp/LabelEdit.js' ;
import { initCompLabelEdit } from './comp/LabelEdit.js' ;
import { initRepoDiffConversationNav } from './repo-diff.js' ;
import { initRepoDiffConversationNav } from './repo-diff.js' ;
import { attachTribute } from './tribute.js' ;
import { createDropzone } from './dropzone.js' ;
import { createDropzone } from './dropzone.js' ;
import { initCommentContent , initMarkupContent } from '../markup/content.js' ;
import { initCommentContent , initMarkupContent } from '../markup/content.js' ;
import { initCompReactionSelector } from './comp/ReactionSelector.js' ;
import { initCompReactionSelector } from './comp/ReactionSelector.js' ;
import { initRepoSettingBranches } from './repo-settings.js' ;
import { initRepoSettingBranches } from './repo-settings.js' ;
import { initRepoPullRequestMergeForm } from './repo-issue-pr-form.js' ;
import { initRepoPullRequestMergeForm } from './repo-issue-pr-form.js' ;
import { hideElem , showElem } from '../utils/dom.js' ;
import { hideElem , showElem } from '../utils/dom.js' ;
import { getComboMarkdownEditor , initComboMarkdownEditor } from './comp/ComboMarkdownEditor.js' ;
import { attachRefIssueContextPopup } from './contextpopup.js' ;
import { attachRefIssueContextPopup } from './contextpopup.js' ;
const { csrfToken } = window . config ;
const { csrfToken } = window . config ;
// if there are draft comments (more than 20 chars) , confirm before reloading, to avoid losing comments
// if there are draft comments, confirm before reloading, to avoid losing comments
function reloadConfirmDraftComment ( ) {
function reloadConfirmDraftComment ( ) {
const commentTextareas = [
const commentTextareas = [
document . querySelector ( '.edit-content-zone:not(.gt-hidden) textarea' ) ,
document . querySelector ( '.edit-content-zone:not(.gt-hidden) textarea' ) ,
document . querySelector ( '.edit_ area' ) ,
document . querySelector ( '#comment-form text area' ) ,
] ;
] ;
for ( const textarea of commentTextareas ) {
for ( const textarea of commentTextareas ) {
// Most users won't feel too sad if they lose a comment with 10 or 20 chars, they can re-type these in seconds.
// Most users won't feel too sad if they lose a comment with 10 chars, they can re-type these in seconds.
// But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
// But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
if ( textarea && textarea . value . trim ( ) . length > 2 0) {
if ( textarea && textarea . value . trim ( ) . length > 1 0) {
textarea . parentElement . scrollIntoView ( ) ;
textarea . parentElement . scrollIntoView ( ) ;
if ( ! window . confirm ( 'Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?' ) ) {
if ( ! window . confirm ( 'Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?' ) ) {
return ;
return ;
@ -85,25 +82,20 @@ export function initRepoCommentForm() {
} ) ;
} ) ;
}
}
( async ( ) => {
const $statusButton = $ ( '#status-button' ) ;
const $statusButton = $ ( '#status-button' ) ;
$statusButton . on ( 'click' , ( e ) => {
for ( const textarea of $commentForm . find ( 'textarea:not(.review-textarea, .no-easymde)' ) ) {
e . preventDefault ( ) ;
// Don't initialize EasyMDE for the dormant #edit-content-form
$ ( '#status' ) . val ( $statusButton . data ( 'status-val' ) ) ;
if ( textarea . closest ( '#edit-content-form' ) ) {
$ ( '#comment-form' ) . trigger ( 'submit' ) ;
continue ;
} ) ;
}
const easyMDE = await createCommentEasyMDE ( textarea , {
const _promise = initComboMarkdownEditor ( $commentForm . find ( '.combo-markdown-editor' ) , {
'onChange' : ( ) => {
onContentChanged ( editor ) {
const value = easyMDE ? . value ( ) . trim ( ) ;
$statusButton . text ( $statusButton . attr ( editor . value ( ) . trim ( ) ? 'data-status-and-comment' : 'data-status' ) ) ;
$statusButton . text ( $statusButton . attr ( value . length === 0 ? 'data-status' : 'data-status-and-comment' ) ) ;
} ,
} ,
} ) ;
} ) ;
initEasyMDEImagePaste ( easyMDE , $commentForm . find ( '.dropzone' ) ) ;
}
} ) ( ) ;
initBranchSelector ( ) ;
initBranchSelector ( ) ;
initCompMarkupContentPreviewTab ( $commentForm ) ;
// List submits
// List submits
function initListSubmits ( selector , outerSelector ) {
function initListSubmits ( selector , outerSelector ) {
@ -275,7 +267,7 @@ export function initRepoCommentForm() {
} else if ( input _id === '#project_id' ) {
} else if ( input _id === '#project_id' ) {
icon = svg ( 'octicon-project' , 18 , 'gt-mr-3' ) ;
icon = svg ( 'octicon-project' , 18 , 'gt-mr-3' ) ;
} else if ( input _id === '#assignee_id' ) {
} else if ( input _id === '#assignee_id' ) {
icon = ` <img class="ui avatar image gt-mr-3" src= ${ $ ( this ) . data ( 'avatar' ) } > ` ;
icon = ` <img class="ui avatar image gt-mr-3" alt="avatar" src= ${ $ ( this ) . data ( 'avatar' ) } > ` ;
}
}
$list . find ( '.selected' ) . html ( `
$list . find ( '.selected' ) . html ( `
@ -322,162 +314,148 @@ async function onEditContent(event) {
const $editContentZone = $segment . find ( '.edit-content-zone' ) ;
const $editContentZone = $segment . find ( '.edit-content-zone' ) ;
const $renderContent = $segment . find ( '.render-content' ) ;
const $renderContent = $segment . find ( '.render-content' ) ;
const $rawContent = $segment . find ( '.raw-content' ) ;
const $rawContent = $segment . find ( '.raw-content' ) ;
let $textarea ;
let easyMDE ;
// Setup new form
let comboMarkdownEditor ;
if ( $editContentZone . html ( ) . length === 0 ) {
$editContentZone . html ( $ ( '#edit-content-form' ) . html ( ) ) ;
const setupDropzone = async ( $dropzone ) => {
$textarea = $editContentZone . find ( 'textarea' ) ;
if ( $dropzone . length === 0 ) return null ;
await attachTribute ( $textarea . get ( ) , { mentions : true , emoji : true } ) ;
$dropzone . data ( 'saved' , false ) ;
let dz ;
const fileUuidDict = { } ;
const $dropzone = $editContentZone . find ( '.dropzone' ) ;
const dz = await createDropzone ( $dropzone [ 0 ] , {
if ( $dropzone . length === 1 ) {
url : $dropzone . data ( 'upload-url' ) ,
$dropzone . data ( 'saved' , false ) ;
headers : { 'X-Csrf-Token' : csrfToken } ,
maxFiles : $dropzone . data ( 'max-file' ) ,
const fileUuidDict = { } ;
maxFilesize : $dropzone . data ( 'max-size' ) ,
dz = await createDropzone ( $dropzone [ 0 ] , {
acceptedFiles : ( [ '*/*' , '' ] . includes ( $dropzone . data ( 'accepts' ) ) ) ? null : $dropzone . data ( 'accepts' ) ,
url : $dropzone . data ( 'upload-url' ) ,
addRemoveLinks : true ,
headers : { 'X-Csrf-Token' : csrfToken } ,
dictDefaultMessage : $dropzone . data ( 'default-message' ) ,
maxFiles : $dropzone . data ( 'max-file' ) ,
dictInvalidFileType : $dropzone . data ( 'invalid-input-type' ) ,
maxFilesize : $dropzone . data ( 'max-size' ) ,
dictFileTooBig : $dropzone . data ( 'file-too-big' ) ,
acceptedFiles : ( [ '*/*' , '' ] . includes ( $dropzone . data ( 'accepts' ) ) ) ? null : $dropzone . data ( 'accepts' ) ,
dictRemoveFile : $dropzone . data ( 'remove-file' ) ,
addRemoveLinks : true ,
timeout : 0 ,
dictDefaultMessage : $dropzone . data ( 'default-message' ) ,
thumbnailMethod : 'contain' ,
dictInvalidFileType : $dropzone . data ( 'invalid-input-type' ) ,
thumbnailWidth : 480 ,
dictFileTooBig : $dropzone . data ( 'file-too-big' ) ,
thumbnailHeight : 480 ,
dictRemoveFile : $dropzone . data ( 'remove-file' ) ,
init ( ) {
timeout : 0 ,
this . on ( 'success' , ( file , data ) => {
thumbnailMethod : 'contain' ,
file . uuid = data . uuid ;
thumbnailWidth : 480 ,
fileUuidDict [ file . uuid ] = { submitted : false } ;
thumbnailHeight : 480 ,
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
init ( ) {
$dropzone . find ( '.files' ) . append ( input ) ;
this . on ( 'success' , ( file , data ) => {
} ) ;
file . uuid = data . uuid ;
this . on ( 'removedfile' , ( file ) => {
fileUuidDict [ file . uuid ] = { submitted : false } ;
$ ( ` # ${ file . uuid } ` ) . remove ( ) ;
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
if ( $dropzone . data ( 'remove-url' ) && ! fileUuidDict [ file . uuid ] . submitted ) {
$dropzone . find ( '.files' ) . append ( input ) ;
$ . post ( $dropzone . data ( 'remove-url' ) , {
} ) ;
file : file . uuid ,
this . on ( 'removedfile' , ( file ) => {
_csrf : csrfToken ,
$ ( ` # ${ file . uuid } ` ) . remove ( ) ;
if ( $dropzone . data ( 'remove-url' ) && ! fileUuidDict [ file . uuid ] . submitted ) {
$ . post ( $dropzone . data ( 'remove-url' ) , {
file : file . uuid ,
_csrf : csrfToken ,
} ) ;
}
} ) ;
this . on ( 'submit' , ( ) => {
$ . each ( fileUuidDict , ( fileUuid ) => {
fileUuidDict [ fileUuid ] . submitted = true ;
} ) ;
} ) ;
}
} ) ;
this . on ( 'submit' , ( ) => {
$ . each ( fileUuidDict , ( fileUuid ) => {
fileUuidDict [ fileUuid ] . submitted = true ;
} ) ;
} ) ;
this . on ( 'reload' , ( ) => {
} ) ;
$ . getJSON ( $editContentZone . data ( 'attachment-url' ) , ( data ) => {
this . on ( 'reload' , ( ) => {
dz . removeAllFiles ( true ) ;
$ . getJSON ( $editContentZone . data ( 'attachment-url' ) , ( data ) => {
$dropzone . find ( '.files' ) . empty ( ) ;
dz . removeAllFiles ( true ) ;
$ . each ( data , function ( ) {
$dropzone . find ( '.files' ) . empty ( ) ;
const imgSrc = ` ${ $dropzone . data ( 'link-url' ) } / ${ this . uuid } ` ;
$ . each ( data , function ( ) {
dz . emit ( 'addedfile' , this ) ;
const imgSrc = ` ${ $dropzone . data ( 'link-url' ) } / ${ this . uuid } ` ;
dz . emit ( 'thumbn ail' , this , imgSrc ) ;
dz . emit ( 'addedf ile ' , this ) ;
dz . emit ( 'complete ' , this ) ;
dz . emit ( 'thumbnail ' , this , imgSrc ) ;
dz . files . push ( this ) ;
dz . emit ( 'complete' , this ) ;
fileUuidDict [ this . uuid ] = { submitted : true } ;
dz . files . push ( this ) ;
$dropzone . find ( ` img[src=' ${ imgSrc } '] ` ) . css ( 'max-width' , '100%' ) ;
fileUuidDict [ this . uuid ] = { submitted : true } ;
const input = $ ( ` <input id=" ${ this . uuid } " name="files" type="hidden"> ` ) . val ( this . uuid ) ;
$dropzone . find ( ` img[src=' ${ imgSrc } '] ` ) . css ( 'max-width' , '100%' ) ;
$dropzone . find ( '.files' ) . append ( input ) ;
const input = $ ( ` <input id=" ${ this . uuid } " name="files" type="hidden"> ` ) . val ( this . uuid ) ;
} ) ;
$dropzone . find ( '.files' ) . append ( input ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ,
} ) ;
} ) ;
} ,
} ) ;
dz . emit ( 'reload' ) ;
return dz ;
} ;
const cancelAndReset = ( dz ) => {
showElem ( $renderContent ) ;
hideElem ( $editContentZone ) ;
if ( dz ) {
dz . emit ( 'reload' ) ;
dz . emit ( 'reload' ) ;
}
}
// Give new write/preview data-tab name to distinguish from others
} ;
const $editContentForm = $editContentZone . find ( '.ui.comment.form' ) ;
const $tabMenu = $editContentForm . find ( '.tabular.menu' ) ;
const saveAndRefresh = ( dz , $dropzone ) => {
$tabMenu . attr ( 'data-write' , $editContentZone . data ( 'write' ) ) ;
showElem ( $renderContent ) ;
$tabMenu . attr ( 'data-preview' , $editContentZone . data ( 'preview' ) ) ;
hideElem ( $editContentZone ) ;
$tabMenu . find ( '.write.item' ) . attr ( 'data-tab' , $editContentZone . data ( 'write' ) ) ;
const $attachments = $dropzone . find ( '.files' ) . find ( '[name=files]' ) . map ( function ( ) {
$tabMenu . find ( '.preview.item' ) . attr ( 'data-tab' , $editContentZone . data ( 'preview' ) ) ;
return $ ( this ) . val ( ) ;
$editContentForm . find ( '.write' ) . attr ( 'data-tab' , $editContentZone . data ( 'write' ) ) ;
} ) . get ( ) ;
$editContentForm . find ( '.preview' ) . attr ( 'data-tab' , $editContentZone . data ( 'preview' ) ) ;
$ . post ( $editContentZone . data ( 'update-url' ) , {
easyMDE = await createCommentEasyMDE ( $textarea ) ;
_csrf : csrfToken ,
content : comboMarkdownEditor . value ( ) ,
initCompMarkupContentPreviewTab ( $editContentForm ) ;
context : $editContentZone . data ( 'context' ) ,
initEasyMDEImagePaste ( easyMDE , $dropzone ) ;
files : $attachments ,
} , ( data ) => {
const $saveButton = $editContentZone . find ( '.save.button' ) ;
if ( ! data . content ) {
$textarea . on ( 'ce-quick-submit' , ( ) => {
$renderContent . html ( $ ( '#no-content' ) . html ( ) ) ;
$saveButton . trigger ( 'click' ) ;
$rawContent . text ( '' ) ;
} ) ;
} else {
$renderContent . html ( data . content ) ;
$rawContent . text ( comboMarkdownEditor . value ( ) ) ;
$editContentZone . find ( '.cancel.button' ) . on ( 'click' , ( e ) => {
const refIssues = $renderContent . find ( 'p .ref-issue' ) ;
e . preventDefault ( ) ;
attachRefIssueContextPopup ( refIssues ) ;
showElem ( $renderContent ) ;
}
hideElem ( $editContentZone ) ;
const $content = $segment ;
if ( ! $content . find ( '.dropzone-attachments' ) . length ) {
if ( data . attachments !== '' ) {
$content . append ( ` <div class="dropzone-attachments"></div> ` ) ;
$content . find ( '.dropzone-attachments' ) . replaceWith ( data . attachments ) ;
}
} else if ( data . attachments === '' ) {
$content . find ( '.dropzone-attachments' ) . remove ( ) ;
} else {
$content . find ( '.dropzone-attachments' ) . replaceWith ( data . attachments ) ;
}
if ( dz ) {
if ( dz ) {
dz . emit ( 'submit' ) ;
dz . emit ( 'reload' ) ;
dz . emit ( 'reload' ) ;
}
}
initMarkupContent ( ) ;
initCommentContent ( ) ;
} ) ;
} ) ;
} ;
$saveButton . on ( 'click' , ( ) => {
if ( ! $editContentZone . html ( ) ) {
showElem ( $renderContent ) ;
$editContentZone . html ( $ ( '#issue-comment-editor-template' ) . html ( ) ) ;
hideElem ( $editContentZone ) ;
comboMarkdownEditor = await initComboMarkdownEditor ( $editContentZone . find ( '.combo-markdown-editor' ) ) ;
const $attachments = $dropzone . find ( '.files' ) . find ( '[name=files]' ) . map ( function ( ) {
return $ ( this ) . val ( ) ;
const $dropzone = $editContentZone . find ( '.dropzone' ) ;
} ) . get ( ) ;
const dz = await setupDropzone ( $dropzone ) ;
$ . post ( $editContentZone . data ( 'update-url' ) , {
$editContentZone . find ( '.cancel.button' ) . on ( 'click' , ( e ) => {
_csrf : csrfToken ,
e . preventDefault ( ) ;
content : $textarea . val ( ) ,
cancelAndReset ( dz ) ;
context : $editContentZone . data ( 'context' ) ,
} ) ;
files : $attachments ,
$editContentZone . find ( '.save.button' ) . on ( 'click' , ( e ) => {
} , ( data ) => {
e . preventDefault ( ) ;
if ( data . length === 0 || data . content . length === 0 ) {
saveAndRefresh ( dz , $dropzone ) ;
$renderContent . html ( $ ( '#no-content' ) . html ( ) ) ;
$rawContent . text ( '' ) ;
} else {
$renderContent . html ( data . content ) ;
$rawContent . text ( $textarea . val ( ) ) ;
const refIssues = $renderContent . find ( 'p .ref-issue' ) ;
attachRefIssueContextPopup ( refIssues ) ;
}
const $content = $segment ;
if ( ! $content . find ( '.dropzone-attachments' ) . length ) {
if ( data . attachments !== '' ) {
$content . append ( ` <div class="dropzone-attachments"></div> ` ) ;
$content . find ( '.dropzone-attachments' ) . replaceWith ( data . attachments ) ;
}
} else if ( data . attachments === '' ) {
$content . find ( '.dropzone-attachments' ) . remove ( ) ;
} else {
$content . find ( '.dropzone-attachments' ) . replaceWith ( data . attachments ) ;
}
if ( dz ) {
dz . emit ( 'submit' ) ;
dz . emit ( 'reload' ) ;
}
initMarkupContent ( ) ;
initCommentContent ( ) ;
} ) ;
} ) ;
} ) ;
} else { // use existing form
} else {
$textarea = $segment . find ( 'textarea' ) ;
comboMarkdownEditor = getComboMarkdownEditor ( $editContentZone . find ( '.combo-markdown-editor' ) ) ;
easyMDE = getAttachedEasyMDE ( $textarea ) ;
}
}
// Show write/preview tab and copy raw content as needed
// Show write/preview tab and copy raw content as needed
showElem ( $editContentZone ) ;
showElem ( $editContentZone ) ;
hideElem ( $renderContent ) ;
hideElem ( $renderContent ) ;
if ( $textarea . val ( ) . length === 0 ) {
if ( ! comboMarkdownEditor . value ( ) ) {
$textarea . val ( $rawContent . text ( ) ) ;
comboMarkdownEditor . value ( $rawContent . text ( ) ) ;
easyMDE . value ( $rawContent . text ( ) ) ;
}
}
requestAnimationFrame ( ( ) => {
comboMarkdownEditor . focus ( ) ;
$textarea . focus ( ) ;
easyMDE . codemirror . focus ( ) ;
} ) ;
}
}
export function initRepository ( ) {
export function initRepository ( ) {
@ -575,7 +553,6 @@ export function initRepository() {
initRepoIssueCommentDelete ( ) ;
initRepoIssueCommentDelete ( ) ;
initRepoIssueDependencyDelete ( ) ;
initRepoIssueDependencyDelete ( ) ;
initRepoIssueCodeCommentCancel ( ) ;
initRepoIssueCodeCommentCancel ( ) ;
initRepoIssueStatusButton ( ) ;
initRepoPullRequestUpdate ( ) ;
initRepoPullRequestUpdate ( ) ;
initCompReactionSelector ( ) ;
initCompReactionSelector ( ) ;
@ -592,12 +569,6 @@ export function initRepository() {
const $form = $repoComparePull . find ( '.pullrequest-form' ) ;
const $form = $repoComparePull . find ( '.pullrequest-form' ) ;
showElem ( $form ) ;
showElem ( $form ) ;
$form . find ( 'textarea.edit_area' ) . each ( function ( ) {
const easyMDE = getAttachedEasyMDE ( $ ( this ) ) ;
if ( easyMDE ) {
easyMDE . codemirror . refresh ( ) ;
}
} ) ;
} ) ;
} ) ;
}
}
@ -614,24 +585,22 @@ function initRepoIssueCommentEdit() {
const target = $ ( this ) . data ( 'target' ) ;
const target = $ ( this ) . data ( 'target' ) ;
const quote = $ ( ` # ${ target } ` ) . text ( ) . replace ( /\n/g , '\n> ' ) ;
const quote = $ ( ` # ${ target } ` ) . text ( ) . replace ( /\n/g , '\n> ' ) ;
const content = ` > ${ quote } \n \n ` ;
const content = ` > ${ quote } \n \n ` ;
let easyMDE ;
let editor ;
if ( $ ( this ) . hasClass ( 'quote-reply-diff' ) ) {
if ( $ ( this ) . hasClass ( 'quote-reply-diff' ) ) {
const $replyBtn = $ ( this ) . closest ( '.comment-code-cloud' ) . find ( 'button.comment-form-reply' ) ;
const $replyBtn = $ ( this ) . closest ( '.comment-code-cloud' ) . find ( 'button.comment-form-reply' ) ;
easyMDE = await handleReply ( $replyBtn ) ;
editor = await handleReply ( $replyBtn ) ;
} else {
} else {
// for normal issue/comment page
// for normal issue/comment page
easyMDE = getAttachedEasyMDE ( $ ( '#comment-form .edit_area ' ) ) ;
editor = getComboMarkdownEditor ( $ ( '#comment-form .combo-markdown-editor ' ) ) ;
}
}
if ( easyMDE ) {
if ( editor ) {
if ( easyMDE . value ( ) !== '' ) {
if ( editor . value ( ) ) {
easyMDE . value ( ` ${ easyMDE . value ( ) } \n \n ${ content } ` ) ;
editor . value ( ` ${ editor . value ( ) } \n \n ${ content } ` ) ;
} else {
} else {
easyMDE . value ( ` ${ content } ` ) ;
editor . value ( content ) ;
}
}
requestAnimationFrame ( ( ) => {
editor . focus ( ) ;
easyMDE . codemirror . focus ( ) ;
editor . moveCursorToEnd ( ) ;
easyMDE . codemirror . setCursor ( easyMDE . codemirror . lineCount ( ) , 0 ) ;
} ) ;
}
}
} ) ;
} ) ;
}
}