2022-01-28 13:00:11 -08:00
import $ from 'jquery' ;
2021-11-17 18:08:25 +00:00
import { htmlEscape } from 'escape-goat' ;
2021-10-17 01:28:04 +08:00
import { createCodeEditor } from './codeeditor.js' ;
2023-02-19 12:06:14 +08:00
import { hideElem , showElem } from '../utils/dom.js' ;
2023-04-12 11:03:23 +08:00
import { initMarkupContent } from '../markup/content.js' ;
import { attachRefIssueContextPopup } from './contextpopup.js' ;
2024-03-07 09:28:33 +02:00
import { POST } from '../modules/fetch.js' ;
2021-10-17 01:28:04 +08:00
function initEditPreviewTab ( $form ) {
const $tabMenu = $form . find ( '.tabular.menu' ) ;
$tabMenu . find ( '.item' ) . tab ( ) ;
const $previewTab = $tabMenu . find ( ` .item[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) ;
if ( $previewTab . length ) {
2024-03-07 09:28:33 +02:00
$previewTab . on ( 'click' , async function ( ) {
2021-10-17 01:28:04 +08:00
const $this = $ ( this ) ;
let context = ` ${ $this . data ( 'context' ) } / ` ;
2023-03-24 07:12:23 +01:00
const mode = $this . data ( 'markup-mode' ) || 'comment' ;
2024-03-16 14:22:16 +02:00
const $treePathEl = $form . find ( 'input#tree_path' ) ;
if ( $treePathEl . length > 0 ) {
context += $treePathEl . val ( ) ;
2021-10-17 01:28:04 +08:00
}
context = context . substring ( 0 , context . lastIndexOf ( '/' ) ) ;
2024-03-07 09:28:33 +02:00
const formData = new FormData ( ) ;
formData . append ( 'mode' , mode ) ;
formData . append ( 'context' , context ) ;
formData . append ( 'text' , $form . find ( ` .tab[data-tab=" ${ $tabMenu . data ( 'write' ) } "] textarea ` ) . val ( ) ) ;
2024-03-16 14:22:16 +02:00
formData . append ( 'file_path' , $treePathEl . val ( ) ) ;
2024-03-07 09:28:33 +02:00
try {
const response = await POST ( $this . data ( 'url' ) , { data : formData } ) ;
const data = await response . text ( ) ;
2021-10-17 01:28:04 +08:00
const $previewPanel = $form . find ( ` .tab[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) ;
2023-04-12 11:03:23 +08:00
renderPreviewPanelContent ( $previewPanel , data ) ;
2024-03-07 09:28:33 +02:00
} catch ( error ) {
console . error ( 'Error:' , error ) ;
}
2021-10-17 01:28:04 +08:00
} ) ;
}
}
function initEditorForm ( ) {
if ( $ ( '.repository .edit.form' ) . length === 0 ) {
return ;
}
initEditPreviewTab ( $ ( '.repository .edit.form' ) ) ;
}
function getCursorPosition ( $e ) {
const el = $e . get ( 0 ) ;
let pos = 0 ;
if ( 'selectionStart' in el ) {
pos = el . selectionStart ;
} else if ( 'selection' in document ) {
el . focus ( ) ;
const Sel = document . selection . createRange ( ) ;
const SelLength = document . selection . createRange ( ) . text . length ;
Sel . moveStart ( 'character' , - el . value . length ) ;
pos = Sel . text . length - SelLength ;
}
return pos ;
}
2021-11-09 17:27:25 +08:00
export function initRepoEditor ( ) {
2021-10-17 01:28:04 +08:00
initEditorForm ( ) ;
$ ( '.js-quick-pull-choice-option' ) . on ( 'change' , function ( ) {
if ( $ ( this ) . val ( ) === 'commit-to-new-branch' ) {
2023-02-19 12:06:14 +08:00
showElem ( $ ( '.quick-pull-branch-name' ) ) ;
2021-10-17 01:28:04 +08:00
$ ( '.quick-pull-branch-name input' ) . prop ( 'required' , true ) ;
} else {
2023-02-19 12:06:14 +08:00
hideElem ( $ ( '.quick-pull-branch-name' ) ) ;
2021-10-17 01:28:04 +08:00
$ ( '.quick-pull-branch-name input' ) . prop ( 'required' , false ) ;
}
$ ( '#commit-button' ) . text ( $ ( this ) . attr ( 'button_text' ) ) ;
} ) ;
2023-03-04 06:28:20 +08:00
const joinTreePath = ( $fileNameEl ) => {
const parts = [ ] ;
$ ( '.breadcrumb span.section' ) . each ( function ( ) {
2024-03-16 14:22:16 +02:00
const $element = $ ( this ) ;
if ( $element . find ( 'a' ) . length ) {
parts . push ( $element . find ( 'a' ) . text ( ) ) ;
2023-03-04 06:28:20 +08:00
} else {
2024-03-16 14:22:16 +02:00
parts . push ( $element . text ( ) ) ;
2023-03-04 06:28:20 +08:00
}
} ) ;
if ( $fileNameEl . val ( ) ) parts . push ( $fileNameEl . val ( ) ) ;
$ ( '#tree_path' ) . val ( parts . join ( '/' ) ) ;
} ;
2021-10-17 01:28:04 +08:00
const $editFilename = $ ( '#file-name' ) ;
2023-03-04 06:28:20 +08:00
$editFilename . on ( 'input' , function ( ) {
const parts = $ ( this ) . val ( ) . split ( '/' ) ;
2021-10-17 01:28:04 +08:00
2023-03-04 06:28:20 +08:00
if ( parts . length > 1 ) {
2021-10-17 01:28:04 +08:00
for ( let i = 0 ; i < parts . length ; ++ i ) {
2023-03-04 06:28:20 +08:00
const value = parts [ i ] ;
2021-10-17 01:28:04 +08:00
if ( i < parts . length - 1 ) {
if ( value . length ) {
2021-11-17 18:08:25 +00:00
$ ( ` <span class="section"><a href="#"> ${ htmlEscape ( value ) } </a></span> ` ) . insertBefore ( $ ( this ) ) ;
2023-08-16 02:08:23 +02:00
$ ( '<div class="breadcrumb-divider">/</div>' ) . insertBefore ( $ ( this ) ) ;
2021-10-17 01:28:04 +08:00
}
} else {
$ ( this ) . val ( value ) ;
}
2023-03-04 06:28:20 +08:00
this . setSelectionRange ( 0 , 0 ) ;
2021-10-17 01:28:04 +08:00
}
}
2023-03-04 06:28:20 +08:00
joinTreePath ( $ ( this ) ) ;
} ) ;
$editFilename . on ( 'keydown' , function ( e ) {
const $section = $ ( '.breadcrumb span.section' ) ;
// Jump back to last directory once the filename is empty
if ( e . code === 'Backspace' && getCursorPosition ( $ ( this ) ) === 0 && $section . length > 0 ) {
e . preventDefault ( ) ;
2023-08-16 02:08:23 +02:00
const $divider = $ ( '.breadcrumb .breadcrumb-divider' ) ;
2023-03-04 06:28:20 +08:00
const value = $section . last ( ) . find ( 'a' ) . text ( ) ;
$ ( this ) . val ( value + $ ( this ) . val ( ) ) ;
this . setSelectionRange ( value . length , value . length ) ;
$section . last ( ) . remove ( ) ;
$divider . last ( ) . remove ( ) ;
joinTreePath ( $ ( this ) ) ;
}
} ) ;
2021-10-17 01:28:04 +08:00
const $editArea = $ ( '.repository.editor textarea#edit_area' ) ;
if ( ! $editArea . length ) return ;
2021-11-09 17:27:25 +08:00
( async ( ) => {
2023-03-24 07:12:23 +01:00
const editor = await createCodeEditor ( $editArea [ 0 ] , $editFilename [ 0 ] ) ;
2021-10-17 01:28:04 +08:00
2021-11-09 17:27:25 +08:00
// Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
// to enable or disable the commit button
const $commitButton = $ ( '#commit-button' ) ;
const $editForm = $ ( '.ui.edit.form' ) ;
const dirtyFileClass = 'dirty-file' ;
2021-10-17 01:28:04 +08:00
2021-11-09 17:27:25 +08:00
// Disabling the button at the start
if ( $ ( 'input[name="page_has_posted"]' ) . val ( ) !== 'true' ) {
$commitButton . prop ( 'disabled' , true ) ;
2021-10-17 01:28:04 +08:00
}
2021-11-09 17:27:25 +08:00
// Registering a custom listener for the file path and the file content
$editForm . areYouSure ( {
silent : true ,
dirtyClass : dirtyFileClass ,
fieldSelector : ':input:not(.commit-form-wrapper :input)' ,
change ( ) {
const dirty = $ ( this ) . hasClass ( dirtyFileClass ) ;
$commitButton . prop ( 'disabled' , ! dirty ) ;
} ,
} ) ;
2021-10-17 01:28:04 +08:00
2021-11-09 17:27:25 +08:00
// Update the editor from query params, if available,
// only after the dirtyFileClass initialization
const params = new URLSearchParams ( window . location . search ) ;
const value = params . get ( 'value' ) ;
if ( value ) {
editor . setValue ( value ) ;
2021-10-17 01:28:04 +08:00
}
2021-11-09 17:27:25 +08:00
$commitButton . on ( 'click' , ( event ) => {
// A modal which asks if an empty file should be committed
if ( $editArea . val ( ) . length === 0 ) {
$ ( '#edit-empty-content-modal' ) . modal ( {
onApprove ( ) {
$ ( '.edit.form' ) . trigger ( 'submit' ) ;
} ,
} ) . modal ( 'show' ) ;
event . preventDefault ( ) ;
}
} ) ;
} ) ( ) ;
2021-10-17 01:28:04 +08:00
}
2023-04-12 11:03:23 +08:00
export function renderPreviewPanelContent ( $panelPreviewer , data ) {
$panelPreviewer . html ( data ) ;
initMarkupContent ( ) ;
2024-03-16 14:22:16 +02:00
const $refIssues = $panelPreviewer . find ( 'p .ref-issue' ) ;
attachRefIssueContextPopup ( $refIssues ) ;
2023-04-12 11:03:23 +08:00
}