2019-11-13 17:52:13 +03:00
/* globals wipPrefixes, issuesTribute, emojiTribute */
2020-02-24 00:34:28 +03:00
/* exported timeAddManual, toggleStopwatch, cancelStopwatch */
2019-11-14 20:58:32 +03:00
/* exported toggleDeadlineForm, setDeadline, updateDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */
2019-11-13 17:52:13 +03:00
2019-11-22 02:30:14 +03:00
import './publicPath.js' ;
2020-01-26 11:17:25 +03:00
import './polyfills.js' ;
2020-02-24 00:34:28 +03:00
import Vue from 'vue' ;
import 'jquery.are-you-sure' ;
2020-02-07 20:09:30 +03:00
import './vendor/semanticDropdown.js' ;
2020-02-12 04:53:18 +03:00
import { svg } from './utils.js' ;
2020-02-07 20:09:30 +03:00
2020-01-29 00:57:20 +03:00
import initContextPopups from './features/contextPopup.js' ;
import initHighlight from './features/highlight.js' ;
2020-02-07 20:09:30 +03:00
import initGitGraph from './features/gitGraph.js' ;
2020-02-08 02:03:42 +03:00
import initClipboard from './features/clipboard.js' ;
2020-02-24 00:34:28 +03:00
import initUserHeatmap from './features/userHeatmap.js' ;
2019-11-18 00:39:06 +03:00
2020-01-20 13:07:30 +03:00
import ActivityTopAuthors from './components/ActivityTopAuthors.vue' ;
2020-02-12 04:53:18 +03:00
const { AppSubUrl , StaticUrlPrefix } = window . config ;
2019-11-13 17:52:13 +03:00
function htmlEncode ( text ) {
2019-11-15 00:39:51 +03:00
return jQuery ( '<div />' ) . text ( text ) . html ( ) ;
2019-11-13 17:52:13 +03:00
}
let csrf ;
let previewFileModes ;
let simpleMDEditor ;
2019-12-16 18:56:35 +03:00
const commentMDEditors = { } ;
2019-11-13 17:52:13 +03:00
let codeMirrorEditor ;
2020-01-29 00:57:20 +03:00
let hljs ;
2019-11-13 17:52:13 +03:00
// Disable Dropzone auto-discover because it's manually initialized
2019-11-15 00:39:51 +03:00
if ( typeof ( Dropzone ) !== 'undefined' ) {
Dropzone . autoDiscover = false ;
2019-11-13 17:52:13 +03:00
}
2020-01-12 05:57:32 +03:00
// Silence fomantic's error logging when tabs are used without a target content element
$ . fn . tab . settings . silent = true ;
2019-11-13 17:52:13 +03:00
function initCommentPreviewTab ( $form ) {
2019-11-15 00:39:51 +03:00
const $tabMenu = $form . find ( '.tabular.menu' ) ;
$tabMenu . find ( '.item' ) . tab ( ) ;
$tabMenu . find ( ` .item[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) . click ( function ( ) {
const $this = $ ( this ) ;
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
mode : 'gfm' ,
context : $this . data ( 'context' ) ,
text : $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'write' ) } "] textarea ` ) . val ( )
} , ( data ) => {
const $previewPanel = $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) ;
$previewPanel . html ( data ) ;
emojify . run ( $previewPanel [ 0 ] ) ;
$ ( 'pre code' , $previewPanel [ 0 ] ) . each ( function ( ) {
hljs . highlightBlock ( this ) ;
} ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
buttonsClickOnEnter ( ) ;
2019-11-13 17:52:13 +03:00
}
function initEditPreviewTab ( $form ) {
2019-11-15 00:39:51 +03:00
const $tabMenu = $form . find ( '.tabular.menu' ) ;
$tabMenu . find ( '.item' ) . tab ( ) ;
const $previewTab = $tabMenu . find ( ` .item[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) ;
if ( $previewTab . length ) {
previewFileModes = $previewTab . data ( 'preview-file-modes' ) . split ( ',' ) ;
$previewTab . click ( function ( ) {
const $this = $ ( this ) ;
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
mode : 'gfm' ,
context : $this . data ( 'context' ) ,
text : $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'write' ) } "] textarea ` ) . val ( )
} , ( data ) => {
const $previewPanel = $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'preview' ) } "] ` ) ;
$previewPanel . html ( data ) ;
emojify . run ( $previewPanel [ 0 ] ) ;
$ ( 'pre code' , $previewPanel [ 0 ] ) . each ( function ( ) {
hljs . highlightBlock ( this ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
}
function initEditDiffTab ( $form ) {
2019-11-15 00:39:51 +03:00
const $tabMenu = $form . find ( '.tabular.menu' ) ;
$tabMenu . find ( '.item' ) . tab ( ) ;
$tabMenu . find ( ` .item[data-tab=" ${ $tabMenu . data ( 'diff' ) } "] ` ) . click ( function ( ) {
const $this = $ ( this ) ;
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
context : $this . data ( 'context' ) ,
content : $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'write' ) } "] textarea ` ) . val ( )
} , ( data ) => {
const $diffPreviewPanel = $form . find ( ` .tab.segment[data-tab=" ${ $tabMenu . data ( 'diff' ) } "] ` ) ;
$diffPreviewPanel . html ( data ) ;
emojify . run ( $diffPreviewPanel [ 0 ] ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
}
function initEditForm ( ) {
2019-11-15 00:39:51 +03:00
if ( $ ( '.edit.form' ) . length === 0 ) {
return ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
initEditPreviewTab ( $ ( '.edit.form' ) ) ;
initEditDiffTab ( $ ( '.edit.form' ) ) ;
2019-11-13 17:52:13 +03:00
}
function initBranchSelector ( ) {
2019-11-15 00:39:51 +03:00
const $selectBranch = $ ( '.ui.select-branch' ) ;
const $branchMenu = $selectBranch . find ( '.reference-list-menu' ) ;
$branchMenu . find ( '.item:not(.no-select)' ) . click ( function ( ) {
const selectedValue = $ ( this ) . data ( 'id' ) ;
$ ( $ ( this ) . data ( 'id-selector' ) ) . val ( selectedValue ) ;
$selectBranch . find ( '.ui .branch-name' ) . text ( selectedValue ) ;
} ) ;
$selectBranch . find ( '.reference.column' ) . click ( function ( ) {
$selectBranch . find ( '.scrolling.reference-list-menu' ) . css ( 'display' , 'none' ) ;
$selectBranch . find ( '.reference .text' ) . removeClass ( 'black' ) ;
$ ( $ ( this ) . data ( 'target' ) ) . css ( 'display' , 'block' ) ;
$ ( this ) . find ( '.text' ) . addClass ( 'black' ) ;
return false ;
} ) ;
2019-11-13 17:52:13 +03:00
}
function updateIssuesMeta ( url , action , issueIds , elementId ) {
2019-11-15 00:39:51 +03:00
return new Promise ( ( ( resolve ) => {
$ . ajax ( {
type : 'POST' ,
url ,
data : {
_csrf : csrf ,
action ,
issue _ids : issueIds ,
id : elementId
} ,
success : resolve
} ) ;
} ) ) ;
2019-11-13 17:52:13 +03:00
}
function initRepoStatusChecker ( ) {
2019-11-15 00:39:51 +03:00
const migrating = $ ( '#repo_migrating' ) ;
$ ( '#repo_migrating_failed' ) . hide ( ) ;
if ( migrating ) {
const repo _name = migrating . attr ( 'repo' ) ;
if ( typeof repo _name === 'undefined' ) {
return ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
$ . ajax ( {
type : 'GET' ,
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } / ${ repo _name } /status ` ,
2019-11-15 00:39:51 +03:00
data : {
_csrf : csrf ,
} ,
complete ( xhr ) {
if ( xhr . status === 200 ) {
if ( xhr . responseJSON ) {
if ( xhr . responseJSON . status === 0 ) {
window . location . reload ( ) ;
return ;
}
setTimeout ( ( ) => {
initRepoStatusChecker ( ) ;
} , 2000 ) ;
return ;
}
}
$ ( '#repo_migrating_progress' ) . hide ( ) ;
$ ( '#repo_migrating_failed' ) . show ( ) ;
}
} ) ;
}
2019-11-13 17:52:13 +03:00
}
function initReactionSelector ( parent ) {
2019-11-15 00:39:51 +03:00
let reactions = '' ;
if ( ! parent ) {
parent = $ ( document ) ;
reactions = '.reactions > ' ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
parent . find ( ` ${ reactions } a.label ` ) . popup ( { position : 'bottom left' , metadata : { content : 'title' , title : 'none' } } ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
parent . find ( ` .select-reaction > .menu > .item, ${ reactions } a.label ` ) . on ( 'click' , function ( e ) {
const vm = this ;
e . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( $ ( this ) . hasClass ( 'disabled' ) ) return ;
const actionURL = $ ( this ) . hasClass ( 'item' )
? $ ( this ) . closest ( '.select-reaction' ) . data ( 'action-url' )
: $ ( this ) . data ( 'action-url' ) ;
const url = ` ${ actionURL } / ${ $ ( this ) . hasClass ( 'blue' ) ? 'unreact' : 'react' } ` ;
$ . ajax ( {
type : 'POST' ,
url ,
data : {
_csrf : csrf ,
content : $ ( this ) . data ( 'content' )
}
} ) . done ( ( resp ) => {
if ( resp && ( resp . html || resp . empty ) ) {
const content = $ ( vm ) . closest ( '.content' ) ;
let react = content . find ( '.segment.reactions' ) ;
if ( ! resp . empty && react . length > 0 ) {
react . remove ( ) ;
}
if ( ! resp . empty ) {
react = $ ( '<div class="ui attached segment reactions"></div>' ) ;
const attachments = content . find ( '.segment.bottom:first' ) ;
if ( attachments . length > 0 ) {
react . insertBefore ( attachments ) ;
} else {
react . appendTo ( content ) ;
}
react . html ( resp . html ) ;
const hasEmoji = react . find ( '.has-emoji' ) ;
for ( let i = 0 ; i < hasEmoji . length ; i ++ ) {
emojify . run ( hasEmoji . get ( i ) ) ;
}
react . find ( '.dropdown' ) . dropdown ( ) ;
initReactionSelector ( react ) ;
}
}
} ) ;
} ) ;
2019-11-13 17:52:13 +03:00
}
function insertAtCursor ( field , value ) {
2019-11-15 00:39:51 +03:00
if ( field . selectionStart || field . selectionStart === 0 ) {
const startPos = field . selectionStart ;
const endPos = field . selectionEnd ;
field . value = field . value . substring ( 0 , startPos )
2019-11-13 17:52:13 +03:00
+ value
+ field . value . substring ( endPos , field . value . length ) ;
2019-11-15 00:39:51 +03:00
field . selectionStart = startPos + value . length ;
field . selectionEnd = startPos + value . length ;
} else {
field . value += value ;
}
2019-11-13 17:52:13 +03:00
}
function replaceAndKeepCursor ( field , oldval , newval ) {
2019-11-15 00:39:51 +03:00
if ( field . selectionStart || field . selectionStart === 0 ) {
const startPos = field . selectionStart ;
const endPos = field . selectionEnd ;
field . value = field . value . replace ( oldval , newval ) ;
field . selectionStart = startPos + newval . length - oldval . length ;
field . selectionEnd = endPos + newval . length - oldval . length ;
} else {
field . value = field . value . replace ( oldval , newval ) ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function retrieveImageFromClipboardAsBlob ( pasteEvent , callback ) {
if ( ! pasteEvent . clipboardData ) {
return ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const { items } = pasteEvent . clipboardData ;
if ( typeof items === 'undefined' ) {
return ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
for ( let i = 0 ; i < items . length ; i ++ ) {
if ( items [ i ] . type . indexOf ( 'image' ) === - 1 ) continue ;
const blob = items [ i ] . getAsFile ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( typeof ( callback ) === 'function' ) {
pasteEvent . preventDefault ( ) ;
pasteEvent . stopPropagation ( ) ;
callback ( blob ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
}
function uploadFile ( file , callback ) {
2019-11-15 00:39:51 +03:00
const xhr = new XMLHttpRequest ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
xhr . onload = function ( ) {
if ( xhr . status === 200 ) {
callback ( xhr . responseText ) ;
}
} ;
2019-11-13 17:52:13 +03:00
2020-02-12 04:53:18 +03:00
xhr . open ( 'post' , ` ${ AppSubUrl } /attachments ` , true ) ;
2019-11-15 00:39:51 +03:00
xhr . setRequestHeader ( 'X-Csrf-Token' , csrf ) ;
const formData = new FormData ( ) ;
formData . append ( 'file' , file , file . name ) ;
xhr . send ( formData ) ;
2019-11-13 17:52:13 +03:00
}
function reload ( ) {
2019-11-15 00:39:51 +03:00
window . location . reload ( ) ;
2019-11-13 17:52:13 +03:00
}
function initImagePaste ( target ) {
2019-11-15 00:39:51 +03:00
target . each ( function ( ) {
const field = this ;
field . addEventListener ( 'paste' , ( event ) => {
retrieveImageFromClipboardAsBlob ( event , ( img ) => {
const name = img . name . substr ( 0 , img . name . lastIndexOf ( '.' ) ) ;
insertAtCursor ( field , ` ![ ${ name } ]() ` ) ;
uploadFile ( img , ( res ) => {
const data = JSON . parse ( res ) ;
2020-02-12 04:53:18 +03:00
replaceAndKeepCursor ( field , ` ![ ${ name } ]() ` , ` ![ ${ name } ]( ${ AppSubUrl } /attachments/ ${ data . uuid } ) ` ) ;
2019-11-15 00:39:51 +03:00
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
$ ( '.files' ) . append ( input ) ;
} ) ;
} ) ;
} , false ) ;
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-12-16 18:56:35 +03:00
function initSimpleMDEImagePaste ( simplemde , files ) {
simplemde . codemirror . on ( 'paste' , ( _ , event ) => {
retrieveImageFromClipboardAsBlob ( event , ( img ) => {
const name = img . name . substr ( 0 , img . name . lastIndexOf ( '.' ) ) ;
uploadFile ( img , ( res ) => {
const data = JSON . parse ( res ) ;
const pos = simplemde . codemirror . getCursor ( ) ;
2020-02-12 04:53:18 +03:00
simplemde . codemirror . replaceRange ( ` ![ ${ name } ]( ${ AppSubUrl } /attachments/ ${ data . uuid } ) ` , pos ) ;
2019-12-16 18:56:35 +03:00
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
files . append ( input ) ;
} ) ;
} ) ;
} ) ;
}
2020-01-14 23:47:38 +03:00
let autoSimpleMDE ;
2019-11-13 17:52:13 +03:00
function initCommentForm ( ) {
2019-11-15 00:39:51 +03:00
if ( $ ( '.comment.form' ) . length === 0 ) {
return ;
}
2020-01-14 23:47:38 +03:00
autoSimpleMDE = setCommentSimpleMDE ( $ ( '.comment.form textarea:not(.review-textarea)' ) ) ;
2019-11-15 00:39:51 +03:00
initBranchSelector ( ) ;
initCommentPreviewTab ( $ ( '.comment.form' ) ) ;
initImagePaste ( $ ( '.comment.form textarea' ) ) ;
// Listsubmit
function initListSubmits ( selector , outerSelector ) {
const $list = $ ( ` .ui. ${ outerSelector } .list ` ) ;
const $noSelect = $list . find ( '.no-select' ) ;
const $listMenu = $ ( ` . ${ selector } .menu ` ) ;
let hasLabelUpdateAction = $listMenu . data ( 'action' ) === 'update' ;
const labels = { } ;
$ ( ` . ${ selector } ` ) . dropdown ( 'setting' , 'onHide' , ( ) => {
hasLabelUpdateAction = $listMenu . data ( 'action' ) === 'update' ; // Update the var
if ( hasLabelUpdateAction ) {
const promises = [ ] ;
Object . keys ( labels ) . forEach ( ( elementId ) => {
const label = labels [ elementId ] ;
const promise = updateIssuesMeta (
label [ 'update-url' ] ,
label . action ,
label [ 'issue-id' ] ,
elementId
) ;
promises . push ( promise ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
Promise . all ( promises ) . then ( reload ) ;
}
} ) ;
$listMenu . find ( '.item:not(.no-select)' ) . click ( function ( ) {
// we don't need the action attribute when updating assignees
if ( selector === 'select-assignees-modify' ) {
// UI magic. We need to do this here, otherwise it would destroy the functionality of
// adding/removing labels
if ( $ ( this ) . hasClass ( 'checked' ) ) {
$ ( this ) . removeClass ( 'checked' ) ;
2020-02-11 20:02:41 +03:00
$ ( this ) . find ( '.octicon-check' ) . addClass ( 'invisible' ) ;
2019-11-15 00:39:51 +03:00
} else {
$ ( this ) . addClass ( 'checked' ) ;
2020-02-11 20:02:41 +03:00
$ ( this ) . find ( '.octicon-check' ) . removeClass ( 'invisible' ) ;
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
updateIssuesMeta (
$listMenu . data ( 'update-url' ) ,
'' ,
$listMenu . data ( 'issue-id' ) ,
$ ( this ) . data ( 'id' )
) ;
$listMenu . data ( 'action' , 'update' ) ; // Update to reload the page when we updated items
return false ;
}
if ( $ ( this ) . hasClass ( 'checked' ) ) {
$ ( this ) . removeClass ( 'checked' ) ;
2020-02-11 20:02:41 +03:00
$ ( this ) . find ( '.octicon-check' ) . addClass ( 'invisible' ) ;
2019-11-15 00:39:51 +03:00
if ( hasLabelUpdateAction ) {
if ( ! ( $ ( this ) . data ( 'id' ) in labels ) ) {
labels [ $ ( this ) . data ( 'id' ) ] = {
'update-url' : $listMenu . data ( 'update-url' ) ,
action : 'detach' ,
'issue-id' : $listMenu . data ( 'issue-id' ) ,
} ;
} else {
delete labels [ $ ( this ) . data ( 'id' ) ] ;
}
}
} else {
$ ( this ) . addClass ( 'checked' ) ;
2020-02-11 20:02:41 +03:00
$ ( this ) . find ( '.octicon-check' ) . removeClass ( 'invisible' ) ;
2019-11-15 00:39:51 +03:00
if ( hasLabelUpdateAction ) {
if ( ! ( $ ( this ) . data ( 'id' ) in labels ) ) {
labels [ $ ( this ) . data ( 'id' ) ] = {
'update-url' : $listMenu . data ( 'update-url' ) ,
action : 'attach' ,
'issue-id' : $listMenu . data ( 'issue-id' ) ,
} ;
} else {
delete labels [ $ ( this ) . data ( 'id' ) ] ;
}
}
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const listIds = [ ] ;
$ ( this ) . parent ( ) . find ( '.item' ) . each ( function ( ) {
if ( $ ( this ) . hasClass ( 'checked' ) ) {
listIds . push ( $ ( this ) . data ( 'id' ) ) ;
$ ( $ ( this ) . data ( 'id-selector' ) ) . removeClass ( 'hide' ) ;
} else {
$ ( $ ( this ) . data ( 'id-selector' ) ) . addClass ( 'hide' ) ;
}
} ) ;
if ( listIds . length === 0 ) {
$noSelect . removeClass ( 'hide' ) ;
} else {
$noSelect . addClass ( 'hide' ) ;
}
$ ( $ ( this ) . parent ( ) . data ( 'id' ) ) . val ( listIds . join ( ',' ) ) ;
return false ;
} ) ;
$listMenu . find ( '.no-select.item' ) . click ( function ( ) {
if ( hasLabelUpdateAction || selector === 'select-assignees-modify' ) {
updateIssuesMeta (
$listMenu . data ( 'update-url' ) ,
'clear' ,
$listMenu . data ( 'issue-id' ) ,
''
) . then ( reload ) ;
}
$ ( this ) . parent ( ) . find ( '.item' ) . each ( function ( ) {
$ ( this ) . removeClass ( 'checked' ) ;
2020-02-11 20:02:41 +03:00
$ ( this ) . find ( '.octicon' ) . addClass ( 'invisible' ) ;
2019-11-15 00:39:51 +03:00
} ) ;
$list . find ( '.item' ) . each ( function ( ) {
$ ( this ) . addClass ( 'hide' ) ;
} ) ;
$noSelect . removeClass ( 'hide' ) ;
$ ( $ ( this ) . parent ( ) . data ( 'id' ) ) . val ( '' ) ;
} ) ;
}
// Init labels and assignees
initListSubmits ( 'select-label' , 'labels' ) ;
initListSubmits ( 'select-assignees' , 'assignees' ) ;
initListSubmits ( 'select-assignees-modify' , 'assignees' ) ;
function selectItem ( select _id , input _id ) {
const $menu = $ ( ` ${ select _id } .menu ` ) ;
const $list = $ ( ` .ui ${ select _id } .list ` ) ;
const hasUpdateAction = $menu . data ( 'action' ) === 'update' ;
$menu . find ( '.item:not(.no-select)' ) . click ( function ( ) {
$ ( this ) . parent ( ) . find ( '.item' ) . each ( function ( ) {
$ ( this ) . removeClass ( 'selected active' ) ;
} ) ;
$ ( this ) . addClass ( 'selected active' ) ;
if ( hasUpdateAction ) {
updateIssuesMeta (
$menu . data ( 'update-url' ) ,
'' ,
$menu . data ( 'issue-id' ) ,
$ ( this ) . data ( 'id' )
) . then ( reload ) ;
}
switch ( input _id ) {
case '#milestone_id' :
$list . find ( '.selected' ) . html ( ` <a class="item" href= ${ $ ( this ) . data ( 'href' ) } > ${
htmlEncode ( $ ( this ) . text ( ) ) } < / a > ` ) ;
break ;
case '#assignee_id' :
$list . find ( '.selected' ) . html ( ` <a class="item" href= ${ $ ( this ) . data ( 'href' ) } > `
+ ` <img class="ui avatar image" src= ${ $ ( this ) . data ( 'avatar' ) } > ${
htmlEncode ( $ ( this ) . text ( ) ) } < / a > ` ) ;
}
$ ( ` .ui ${ select _id } .list .no-select ` ) . addClass ( 'hide' ) ;
$ ( input _id ) . val ( $ ( this ) . data ( 'id' ) ) ;
} ) ;
$menu . find ( '.no-select.item' ) . click ( function ( ) {
$ ( this ) . parent ( ) . find ( '.item:not(.no-select)' ) . each ( function ( ) {
$ ( this ) . removeClass ( 'selected active' ) ;
} ) ;
if ( hasUpdateAction ) {
updateIssuesMeta (
$menu . data ( 'update-url' ) ,
'' ,
$menu . data ( 'issue-id' ) ,
$ ( this ) . data ( 'id' )
) . then ( reload ) ;
}
$list . find ( '.selected' ) . html ( '' ) ;
$list . find ( '.no-select' ) . removeClass ( 'hide' ) ;
$ ( input _id ) . val ( '' ) ;
} ) ;
}
// Milestone and assignee
selectItem ( '.select-milestone' , '#milestone_id' ) ;
selectItem ( '.select-assignee' , '#assignee_id' ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initInstall ( ) {
if ( $ ( '.install' ) . length === 0 ) {
return ;
}
if ( $ ( '#db_host' ) . val ( ) === '' ) {
$ ( '#db_host' ) . val ( '127.0.0.1:3306' ) ;
$ ( '#db_user' ) . val ( 'gitea' ) ;
$ ( '#db_name' ) . val ( 'gitea' ) ;
}
// Database type change detection.
$ ( '#db_type' ) . change ( function ( ) {
const sqliteDefault = 'data/gitea.db' ;
const tidbDefault = 'data/gitea_tidb' ;
const dbType = $ ( this ) . val ( ) ;
if ( dbType === 'SQLite3' ) {
$ ( '#sql_settings' ) . hide ( ) ;
$ ( '#pgsql_settings' ) . hide ( ) ;
$ ( '#mysql_settings' ) . hide ( ) ;
$ ( '#sqlite_settings' ) . show ( ) ;
if ( dbType === 'SQLite3' && $ ( '#db_path' ) . val ( ) === tidbDefault ) {
$ ( '#db_path' ) . val ( sqliteDefault ) ;
}
return ;
}
const dbDefaults = {
MySQL : '127.0.0.1:3306' ,
PostgreSQL : '127.0.0.1:5432' ,
MSSQL : '127.0.0.1:1433'
} ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$ ( '#sqlite_settings' ) . hide ( ) ;
$ ( '#sql_settings' ) . show ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$ ( '#pgsql_settings' ) . toggle ( dbType === 'PostgreSQL' ) ;
$ ( '#mysql_settings' ) . toggle ( dbType === 'MySQL' ) ;
$ . each ( dbDefaults , ( _type , defaultHost ) => {
if ( $ ( '#db_host' ) . val ( ) === defaultHost ) {
$ ( '#db_host' ) . val ( dbDefaults [ dbType ] ) ;
return false ;
}
} ) ;
} ) ;
// TODO: better handling of exclusive relations.
$ ( '#offline-mode input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '#disable-gravatar' ) . checkbox ( 'check' ) ;
$ ( '#federated-avatar-lookup' ) . checkbox ( 'uncheck' ) ;
}
} ) ;
$ ( '#disable-gravatar input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '#federated-avatar-lookup' ) . checkbox ( 'uncheck' ) ;
} else {
$ ( '#offline-mode' ) . checkbox ( 'uncheck' ) ;
}
} ) ;
$ ( '#federated-avatar-lookup input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '#disable-gravatar' ) . checkbox ( 'uncheck' ) ;
$ ( '#offline-mode' ) . checkbox ( 'uncheck' ) ;
}
} ) ;
$ ( '#enable-openid-signin input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
if ( ! $ ( '#disable-registration input' ) . is ( ':checked' ) ) {
$ ( '#enable-openid-signup' ) . checkbox ( 'check' ) ;
}
} else {
$ ( '#enable-openid-signup' ) . checkbox ( 'uncheck' ) ;
}
} ) ;
$ ( '#disable-registration input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '#enable-captcha' ) . checkbox ( 'uncheck' ) ;
$ ( '#enable-openid-signup' ) . checkbox ( 'uncheck' ) ;
} else {
$ ( '#enable-openid-signup' ) . checkbox ( 'check' ) ;
}
} ) ;
$ ( '#enable-captcha input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '#disable-registration' ) . checkbox ( 'uncheck' ) ;
}
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-25 14:50:46 +03:00
function initIssueComments ( ) {
if ( $ ( '.repository.view.issue .comments' ) . length === 0 ) return ;
$ ( document ) . click ( ( event ) => {
const urlTarget = $ ( ':target' ) ;
if ( urlTarget . length === 0 ) return ;
const urlTargetId = urlTarget . attr ( 'id' ) ;
if ( ! urlTargetId ) return ;
if ( ! /^(issue|pull)(comment)?-\d+$/ . test ( urlTargetId ) ) return ;
const $target = $ ( event . target ) ;
if ( $target . closest ( ` # ${ urlTargetId } ` ) . length === 0 ) {
const scrollPosition = $ ( window ) . scrollTop ( ) ;
window . location . hash = '' ;
$ ( window ) . scrollTop ( scrollPosition ) ;
window . history . pushState ( null , null , ' ' ) ;
}
} ) ;
}
2019-11-15 00:39:51 +03:00
function initRepository ( ) {
if ( $ ( '.repository' ) . length === 0 ) {
return ;
}
function initFilterSearchDropdown ( selector ) {
const $dropdown = $ ( selector ) ;
$dropdown . dropdown ( {
fullTextSearch : true ,
selectOnKeydown : false ,
onChange ( _text , _value , $choice ) {
if ( $choice . data ( 'url' ) ) {
window . location . href = $choice . data ( 'url' ) ;
}
} ,
message : { noResults : $dropdown . data ( 'no-results' ) }
} ) ;
}
// File list and commits
if ( $ ( '.repository.file.list' ) . length > 0 || ( '.repository.commits' ) . length > 0 ) {
initFilterBranchTagDropdown ( '.choose.reference .dropdown' ) ;
}
// Wiki
if ( $ ( '.repository.wiki.view' ) . length > 0 ) {
initFilterSearchDropdown ( '.choose.page .dropdown' ) ;
}
// Options
if ( $ ( '.repository.settings.options' ) . length > 0 ) {
$ ( '#repo_name' ) . keyup ( function ( ) {
const $prompt = $ ( '#repo-name-change-prompt' ) ;
if ( $ ( this ) . val ( ) . toString ( ) . toLowerCase ( ) !== $ ( this ) . data ( 'name' ) . toString ( ) . toLowerCase ( ) ) {
$prompt . show ( ) ;
} else {
$prompt . hide ( ) ;
}
} ) ;
// Enable or select internal/external wiki system and issue tracker.
$ ( '.enable-system' ) . change ( function ( ) {
if ( this . checked ) {
$ ( $ ( this ) . data ( 'target' ) ) . removeClass ( 'disabled' ) ;
if ( ! $ ( this ) . data ( 'context' ) ) $ ( $ ( this ) . data ( 'context' ) ) . addClass ( 'disabled' ) ;
} else {
$ ( $ ( this ) . data ( 'target' ) ) . addClass ( 'disabled' ) ;
if ( ! $ ( this ) . data ( 'context' ) ) $ ( $ ( this ) . data ( 'context' ) ) . removeClass ( 'disabled' ) ;
}
} ) ;
$ ( '.enable-system-radio' ) . change ( function ( ) {
if ( this . value === 'false' ) {
$ ( $ ( this ) . data ( 'target' ) ) . addClass ( 'disabled' ) ;
if ( typeof $ ( this ) . data ( 'context' ) !== 'undefined' ) $ ( $ ( this ) . data ( 'context' ) ) . removeClass ( 'disabled' ) ;
} else if ( this . value === 'true' ) {
$ ( $ ( this ) . data ( 'target' ) ) . removeClass ( 'disabled' ) ;
if ( typeof $ ( this ) . data ( 'context' ) !== 'undefined' ) $ ( $ ( this ) . data ( 'context' ) ) . addClass ( 'disabled' ) ;
}
} ) ;
}
// Labels
if ( $ ( '.repository.labels' ) . length > 0 ) {
// Create label
const $newLabelPanel = $ ( '.new-label.segment' ) ;
$ ( '.new-label.button' ) . click ( ( ) => {
$newLabelPanel . show ( ) ;
} ) ;
$ ( '.new-label.segment .cancel' ) . click ( ( ) => {
$newLabelPanel . hide ( ) ;
} ) ;
$ ( '.color-picker' ) . each ( function ( ) {
$ ( this ) . minicolors ( ) ;
} ) ;
$ ( '.precolors .color' ) . click ( function ( ) {
const color _hex = $ ( this ) . data ( 'color-hex' ) ;
$ ( '.color-picker' ) . val ( color _hex ) ;
$ ( '.minicolors-swatch-color' ) . css ( 'background-color' , color _hex ) ;
} ) ;
$ ( '.edit-label-button' ) . click ( function ( ) {
$ ( '#label-modal-id' ) . val ( $ ( this ) . data ( 'id' ) ) ;
$ ( '.edit-label .new-label-input' ) . val ( $ ( this ) . data ( 'title' ) ) ;
$ ( '.edit-label .new-label-desc-input' ) . val ( $ ( this ) . data ( 'description' ) ) ;
$ ( '.edit-label .color-picker' ) . val ( $ ( this ) . data ( 'color' ) ) ;
$ ( '.minicolors-swatch-color' ) . css ( 'background-color' , $ ( this ) . data ( 'color' ) ) ;
$ ( '.edit-label.modal' ) . modal ( {
onApprove ( ) {
$ ( '.edit-label.form' ) . submit ( ) ;
}
} ) . modal ( 'show' ) ;
return false ;
} ) ;
}
// Milestones
if ( $ ( '.repository.new.milestone' ) . length > 0 ) {
const $datepicker = $ ( '.milestone.datepicker' ) ;
$datepicker . datetimepicker ( {
lang : $datepicker . data ( 'lang' ) ,
inline : true ,
timepicker : false ,
startDate : $datepicker . data ( 'start-date' ) ,
formatDate : 'Y-m-d' ,
onSelectDate ( ct ) {
$ ( '#deadline' ) . val ( ct . dateFormat ( 'Y-m-d' ) ) ;
}
} ) ;
$ ( '#clear-date' ) . click ( ( ) => {
$ ( '#deadline' ) . val ( '' ) ;
return false ;
} ) ;
}
// Issues
if ( $ ( '.repository.view.issue' ) . length > 0 ) {
// Edit issue title
const $issueTitle = $ ( '#issue-title' ) ;
const $editInput = $ ( '#edit-title-input input' ) ;
const editTitleToggle = function ( ) {
$issueTitle . toggle ( ) ;
$ ( '.not-in-edit' ) . toggle ( ) ;
$ ( '#edit-title-input' ) . toggle ( ) ;
2019-12-16 09:20:25 +03:00
$ ( '#pull-desc' ) . toggle ( ) ;
$ ( '#pull-desc-edit' ) . toggle ( ) ;
2019-11-15 00:39:51 +03:00
$ ( '.in-edit' ) . toggle ( ) ;
$editInput . focus ( ) ;
return false ;
} ;
2019-12-16 09:20:25 +03:00
const changeBranchSelect = function ( ) {
const selectionTextField = $ ( '#pull-target-branch' ) ;
const baseName = selectionTextField . data ( 'basename' ) ;
const branchNameNew = $ ( this ) . data ( 'branch' ) ;
const branchNameOld = selectionTextField . data ( 'branch' ) ;
// Replace branch name to keep translation from HTML template
selectionTextField . html ( selectionTextField . html ( ) . replace (
` ${ baseName } : ${ branchNameOld } ` ,
` ${ baseName } : ${ branchNameNew } `
) ) ;
selectionTextField . data ( 'branch' , branchNameNew ) ; // update branch name in setting
} ;
$ ( '#branch-select > .item' ) . click ( changeBranchSelect ) ;
2019-11-15 00:39:51 +03:00
$ ( '#edit-title' ) . click ( editTitleToggle ) ;
$ ( '#cancel-edit-title' ) . click ( editTitleToggle ) ;
$ ( '#save-edit-title' ) . click ( editTitleToggle ) . click ( function ( ) {
2019-12-16 09:20:25 +03:00
const pullrequest _targetbranch _change = function ( update _url ) {
const targetBranch = $ ( '#pull-target-branch' ) . data ( 'branch' ) ;
const $branchTarget = $ ( '#branch_target' ) ;
if ( targetBranch === $branchTarget . text ( ) ) {
return false ;
}
$ . post ( update _url , {
_csrf : csrf ,
target _branch : targetBranch
} )
. success ( ( data ) => {
$branchTarget . text ( data . base _branch ) ;
} )
. always ( ( ) => {
reload ( ) ;
} ) ;
} ;
const pullrequest _target _update _url = $ ( this ) . data ( 'target-update-url' ) ;
2019-11-15 00:39:51 +03:00
if ( $editInput . val ( ) . length === 0 || $editInput . val ( ) === $issueTitle . text ( ) ) {
$editInput . val ( $issueTitle . text ( ) ) ;
2019-12-16 09:20:25 +03:00
pullrequest _targetbranch _change ( pullrequest _target _update _url ) ;
} else {
$ . post ( $ ( this ) . data ( 'update-url' ) , {
_csrf : csrf ,
title : $editInput . val ( )
} ,
( data ) => {
$editInput . val ( data . title ) ;
$issueTitle . text ( data . title ) ;
pullrequest _targetbranch _change ( pullrequest _target _update _url ) ;
reload ( ) ;
} ) ;
2019-11-15 00:39:51 +03:00
}
return false ;
} ) ;
2019-11-25 14:50:46 +03:00
// Issue Comments
initIssueComments ( ) ;
2019-11-19 21:33:42 +03:00
// Issue/PR Context Menus
$ ( '.context-dropdown' ) . dropdown ( {
action : 'hide'
} ) ;
// Quote reply
$ ( '.quote-reply' ) . click ( function ( event ) {
$ ( this ) . closest ( '.dropdown' ) . find ( '.menu' ) . toggle ( 'visible' ) ;
const target = $ ( this ) . data ( 'target' ) ;
2020-01-14 23:47:38 +03:00
const quote = $ ( ` #comment- ${ target } ` ) . text ( ) . replace ( /\n/g , '\n> ' ) ;
const content = ` > ${ quote } \n \n ` ;
2019-11-19 21:33:42 +03:00
let $content ;
if ( $ ( this ) . hasClass ( 'quote-reply-diff' ) ) {
const $parent = $ ( this ) . closest ( '.comment-code-cloud' ) ;
$parent . find ( 'button.comment-form-reply' ) . click ( ) ;
$content = $parent . find ( '[name="content"]' ) ;
2020-01-14 23:47:38 +03:00
if ( $content . val ( ) !== '' ) {
$content . val ( ` ${ $content . val ( ) } \n \n ${ content } ` ) ;
} else {
$content . val ( ` ${ content } ` ) ;
}
$content . focus ( ) ;
} else if ( autoSimpleMDE !== null ) {
if ( autoSimpleMDE . value ( ) !== '' ) {
autoSimpleMDE . value ( ` ${ autoSimpleMDE . value ( ) } \n \n ${ content } ` ) ;
} else {
autoSimpleMDE . value ( ` ${ content } ` ) ;
}
2019-11-19 21:33:42 +03:00
}
event . preventDefault ( ) ;
} ) ;
2019-11-15 00:39:51 +03:00
// Edit issue or comment content
2019-11-19 21:33:42 +03:00
$ ( '.edit-content' ) . click ( function ( event ) {
$ ( this ) . closest ( '.dropdown' ) . find ( '.menu' ) . toggle ( 'visible' ) ;
const $segment = $ ( this ) . closest ( '.header' ) . next ( ) ;
2019-11-15 00:39:51 +03:00
const $editContentZone = $segment . find ( '.edit-content-zone' ) ;
const $renderContent = $segment . find ( '.render-content' ) ;
const $rawContent = $segment . find ( '.raw-content' ) ;
let $textarea ;
2019-12-16 18:56:35 +03:00
let $simplemde ;
2019-11-15 00:39:51 +03:00
// Setup new form
if ( $editContentZone . html ( ) . length === 0 ) {
$editContentZone . html ( $ ( '#edit-content-form' ) . html ( ) ) ;
$textarea = $editContentZone . find ( 'textarea' ) ;
issuesTribute . attach ( $textarea . get ( ) ) ;
emojiTribute . attach ( $textarea . get ( ) ) ;
const $dropzone = $editContentZone . find ( '.dropzone' ) ;
$dropzone . data ( 'saved' , false ) ;
const $files = $editContentZone . find ( '.comment-files' ) ;
if ( $dropzone . length > 0 ) {
const filenameDict = { } ;
$dropzone . dropzone ( {
url : $dropzone . data ( 'upload-url' ) ,
headers : { 'X-Csrf-Token' : csrf } ,
maxFiles : $dropzone . data ( 'max-file' ) ,
maxFilesize : $dropzone . data ( 'max-size' ) ,
acceptedFiles : ( $dropzone . data ( 'accepts' ) === '*/*' ) ? null : $dropzone . data ( 'accepts' ) ,
addRemoveLinks : true ,
dictDefaultMessage : $dropzone . data ( 'default-message' ) ,
dictInvalidFileType : $dropzone . data ( 'invalid-input-type' ) ,
dictFileTooBig : $dropzone . data ( 'file-too-big' ) ,
dictRemoveFile : $dropzone . data ( 'remove-file' ) ,
init ( ) {
this . on ( 'success' , ( file , data ) => {
filenameDict [ file . name ] = {
uuid : data . uuid ,
submitted : false
} ;
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
$files . append ( input ) ;
} ) ;
this . on ( 'removedfile' , ( file ) => {
if ( ! ( file . name in filenameDict ) ) {
return ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
$ ( ` # ${ filenameDict [ file . name ] . uuid } ` ) . remove ( ) ;
if ( $dropzone . data ( 'remove-url' ) && $dropzone . data ( 'csrf' ) && ! filenameDict [ file . name ] . submitted ) {
$ . post ( $dropzone . data ( 'remove-url' ) , {
file : filenameDict [ file . name ] . uuid ,
_csrf : $dropzone . data ( 'csrf' )
} ) ;
}
} ) ;
this . on ( 'submit' , ( ) => {
$ . each ( filenameDict , ( name ) => {
filenameDict [ name ] . submitted = true ;
} ) ;
} ) ;
this . on ( 'reload' , ( ) => {
$ . getJSON ( $editContentZone . data ( 'attachment-url' ) , ( data ) => {
const drop = $dropzone . get ( 0 ) . dropzone ;
drop . removeAllFiles ( true ) ;
$files . empty ( ) ;
$ . each ( data , function ( ) {
const imgSrc = ` ${ $dropzone . data ( 'upload-url' ) } / ${ this . uuid } ` ;
drop . emit ( 'addedfile' , this ) ;
drop . emit ( 'thumbnail' , this , imgSrc ) ;
drop . emit ( 'complete' , this ) ;
drop . files . push ( this ) ;
filenameDict [ this . name ] = {
submitted : true ,
uuid : this . uuid
} ;
$dropzone . find ( ` img[src=' ${ imgSrc } '] ` ) . css ( 'max-width' , '100%' ) ;
const input = $ ( ` <input id=" ${ this . uuid } " name="files" type="hidden"> ` ) . val ( this . uuid ) ;
$files . append ( input ) ;
} ) ;
} ) ;
} ) ;
}
} ) ;
$dropzone . get ( 0 ) . dropzone . 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' ) ;
$tabMenu . attr ( 'data-write' , $editContentZone . data ( 'write' ) ) ;
$tabMenu . attr ( 'data-preview' , $editContentZone . data ( 'preview' ) ) ;
$tabMenu . find ( '.write.item' ) . attr ( 'data-tab' , $editContentZone . data ( 'write' ) ) ;
$tabMenu . find ( '.preview.item' ) . attr ( 'data-tab' , $editContentZone . data ( 'preview' ) ) ;
$editContentForm . find ( '.write.segment' ) . attr ( 'data-tab' , $editContentZone . data ( 'write' ) ) ;
$editContentForm . find ( '.preview.segment' ) . attr ( 'data-tab' , $editContentZone . data ( 'preview' ) ) ;
2019-12-16 18:56:35 +03:00
$simplemde = setCommentSimpleMDE ( $textarea ) ;
commentMDEditors [ $editContentZone . data ( 'write' ) ] = $simplemde ;
2019-11-15 00:39:51 +03:00
initCommentPreviewTab ( $editContentForm ) ;
2019-12-16 18:56:35 +03:00
initSimpleMDEImagePaste ( $simplemde , $files ) ;
2019-11-15 00:39:51 +03:00
$editContentZone . find ( '.cancel.button' ) . click ( ( ) => {
$renderContent . show ( ) ;
$editContentZone . hide ( ) ;
$dropzone . get ( 0 ) . dropzone . emit ( 'reload' ) ;
} ) ;
$editContentZone . find ( '.save.button' ) . click ( ( ) => {
$renderContent . show ( ) ;
$editContentZone . hide ( ) ;
const $attachments = $files . find ( '[name=files]' ) . map ( function ( ) {
return $ ( this ) . val ( ) ;
} ) . get ( ) ;
$ . post ( $editContentZone . data ( 'update-url' ) , {
_csrf : csrf ,
content : $textarea . val ( ) ,
context : $editContentZone . data ( 'context' ) ,
files : $attachments
} , ( data ) => {
if ( data . length === 0 ) {
$renderContent . html ( $ ( '#no-content' ) . html ( ) ) ;
} else {
$renderContent . html ( data . content ) ;
emojify . run ( $renderContent [ 0 ] ) ;
$ ( 'pre code' , $renderContent [ 0 ] ) . each ( function ( ) {
hljs . highlightBlock ( this ) ;
} ) ;
}
const $content = $segment . parent ( ) ;
if ( ! $content . find ( '.ui.small.images' ) . length ) {
if ( data . attachments !== '' ) {
$content . append (
'<div class="ui bottom attached segment"><div class="ui small images"></div></div>'
) ;
$content . find ( '.ui.small.images' ) . html ( data . attachments ) ;
}
} else if ( data . attachments === '' ) {
$content . find ( '.ui.small.images' ) . parent ( ) . remove ( ) ;
2019-11-13 17:52:13 +03:00
} else {
2019-11-15 00:39:51 +03:00
$content . find ( '.ui.small.images' ) . html ( data . attachments ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
$dropzone . get ( 0 ) . dropzone . emit ( 'submit' ) ;
$dropzone . get ( 0 ) . dropzone . emit ( 'reload' ) ;
} ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} else {
$textarea = $segment . find ( 'textarea' ) ;
2019-12-16 18:56:35 +03:00
$simplemde = commentMDEditors [ $editContentZone . data ( 'write' ) ] ;
2019-11-15 00:39:51 +03:00
}
// Show write/preview tab and copy raw content as needed
$editContentZone . show ( ) ;
$renderContent . hide ( ) ;
if ( $textarea . val ( ) . length === 0 ) {
$textarea . val ( $rawContent . text ( ) ) ;
2019-12-16 18:56:35 +03:00
$simplemde . value ( $rawContent . text ( ) ) ;
2019-11-15 00:39:51 +03:00
}
$textarea . focus ( ) ;
2019-12-16 18:56:35 +03:00
$simplemde . codemirror . focus ( ) ;
2019-11-19 21:33:42 +03:00
event . preventDefault ( ) ;
2019-11-15 00:39:51 +03:00
} ) ;
// Delete comment
$ ( '.delete-comment' ) . click ( function ( ) {
const $this = $ ( this ) ;
if ( window . confirm ( $this . data ( 'locale' ) ) ) {
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf
} ) . success ( ( ) => {
$ ( ` # ${ $this . data ( 'comment-id' ) } ` ) . remove ( ) ;
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
}
return false ;
} ) ;
// Change status
const $statusButton = $ ( '#status-button' ) ;
$ ( '#comment-form .edit_area' ) . keyup ( function ( ) {
if ( $ ( this ) . val ( ) . length === 0 ) {
$statusButton . text ( $statusButton . data ( 'status' ) ) ;
} else {
$statusButton . text ( $statusButton . data ( 'status-and-comment' ) ) ;
}
} ) ;
$statusButton . click ( ( ) => {
$ ( '#status' ) . val ( $statusButton . data ( 'status-val' ) ) ;
$ ( '#comment-form' ) . submit ( ) ;
} ) ;
// Pull Request merge button
const $mergeButton = $ ( '.merge-button > button' ) ;
$mergeButton . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$ ( ` . ${ $ ( this ) . data ( 'do' ) } -fields ` ) . show ( ) ;
$ ( this ) . parent ( ) . hide ( ) ;
} ) ;
$ ( '.merge-button > .dropdown' ) . dropdown ( {
onChange ( _text , _value , $choice ) {
if ( $choice . data ( 'do' ) ) {
$mergeButton . find ( '.button-text' ) . text ( $choice . text ( ) ) ;
$mergeButton . data ( 'do' , $choice . data ( 'do' ) ) ;
}
}
} ) ;
$ ( '.merge-cancel' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$ ( this ) . closest ( '.form' ) . hide ( ) ;
$mergeButton . parent ( ) . show ( ) ;
} ) ;
initReactionSelector ( ) ;
}
// Diff
if ( $ ( '.repository.diff' ) . length > 0 ) {
$ ( '.diff-counter' ) . each ( function ( ) {
const $item = $ ( this ) ;
const addLine = $item . find ( 'span[data-line].add' ) . data ( 'line' ) ;
const delLine = $item . find ( 'span[data-line].del' ) . data ( 'line' ) ;
const addPercent = parseFloat ( addLine ) / ( parseFloat ( addLine ) + parseFloat ( delLine ) ) * 100 ;
$item . find ( '.bar .add' ) . css ( 'width' , ` ${ addPercent } % ` ) ;
} ) ;
}
// Quick start and repository home
$ ( '#repo-clone-ssh' ) . click ( function ( ) {
$ ( '.clone-url' ) . text ( $ ( this ) . data ( 'link' ) ) ;
$ ( '#repo-clone-url' ) . val ( $ ( this ) . data ( 'link' ) ) ;
$ ( this ) . addClass ( 'blue' ) ;
$ ( '#repo-clone-https' ) . removeClass ( 'blue' ) ;
localStorage . setItem ( 'repo-clone-protocol' , 'ssh' ) ;
} ) ;
$ ( '#repo-clone-https' ) . click ( function ( ) {
$ ( '.clone-url' ) . text ( $ ( this ) . data ( 'link' ) ) ;
$ ( '#repo-clone-url' ) . val ( $ ( this ) . data ( 'link' ) ) ;
$ ( this ) . addClass ( 'blue' ) ;
$ ( '#repo-clone-ssh' ) . removeClass ( 'blue' ) ;
localStorage . setItem ( 'repo-clone-protocol' , 'https' ) ;
} ) ;
$ ( '#repo-clone-url' ) . click ( function ( ) {
$ ( this ) . select ( ) ;
} ) ;
// Pull request
const $repoComparePull = $ ( '.repository.compare.pull' ) ;
if ( $repoComparePull . length > 0 ) {
initFilterSearchDropdown ( '.choose.branch .dropdown' ) ;
// show pull request form
$repoComparePull . find ( 'button.show-form' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$repoComparePull . find ( '.pullrequest-form' ) . show ( ) ;
$ ( this ) . parent ( ) . hide ( ) ;
} ) ;
}
// Branches
if ( $ ( '.repository.settings.branches' ) . length > 0 ) {
initFilterSearchDropdown ( '.protected-branches .dropdown' ) ;
2020-02-18 21:58:16 +03:00
$ ( '.enable-protection, .enable-whitelist, .enable-statuscheck' ) . change ( function ( ) {
2019-11-15 00:39:51 +03:00
if ( this . checked ) {
$ ( $ ( this ) . data ( 'target' ) ) . removeClass ( 'disabled' ) ;
} else {
$ ( $ ( this ) . data ( 'target' ) ) . addClass ( 'disabled' ) ;
}
} ) ;
2019-12-04 04:08:56 +03:00
$ ( '.disable-whitelist' ) . change ( function ( ) {
if ( this . checked ) {
$ ( $ ( this ) . data ( 'target' ) ) . addClass ( 'disabled' ) ;
}
} ) ;
2019-11-15 00:39:51 +03:00
}
2020-02-11 12:34:17 +03:00
// Language stats
if ( $ ( '.language-stats' ) . length > 0 ) {
$ ( '.language-stats' ) . on ( 'click' , ( e ) => {
e . preventDefault ( ) ;
$ ( '.language-stats-details, .repository-menu' ) . slideToggle ( ) ;
} ) ;
}
2019-11-15 00:39:51 +03:00
}
function initMigration ( ) {
const toggleMigrations = function ( ) {
const authUserName = $ ( '#auth_username' ) . val ( ) ;
const cloneAddr = $ ( '#clone_addr' ) . val ( ) ;
if ( ! $ ( '#mirror' ) . is ( ':checked' ) && ( authUserName && authUserName . length > 0 )
&& ( cloneAddr !== undefined && ( cloneAddr . startsWith ( 'https://github.com' ) || cloneAddr . startsWith ( 'http://github.com' ) ) ) ) {
$ ( '#migrate_items' ) . show ( ) ;
} else {
$ ( '#migrate_items' ) . hide ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
toggleMigrations ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$ ( '#clone_addr' ) . on ( 'input' , toggleMigrations ) ;
$ ( '#auth_username' ) . on ( 'input' , toggleMigrations ) ;
$ ( '#mirror' ) . on ( 'change' , toggleMigrations ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initPullRequestReview ( ) {
$ ( '.show-outdated' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
const id = $ ( this ) . data ( 'comment' ) ;
$ ( this ) . addClass ( 'hide' ) ;
$ ( ` #code-comments- ${ id } ` ) . removeClass ( 'hide' ) ;
$ ( ` #code-preview- ${ id } ` ) . removeClass ( 'hide' ) ;
$ ( ` #hide-outdated- ${ id } ` ) . removeClass ( 'hide' ) ;
} ) ;
$ ( '.hide-outdated' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
const id = $ ( this ) . data ( 'comment' ) ;
$ ( this ) . addClass ( 'hide' ) ;
$ ( ` #code-comments- ${ id } ` ) . addClass ( 'hide' ) ;
$ ( ` #code-preview- ${ id } ` ) . addClass ( 'hide' ) ;
$ ( ` #show-outdated- ${ id } ` ) . removeClass ( 'hide' ) ;
} ) ;
$ ( 'button.comment-form-reply' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$ ( this ) . hide ( ) ;
const form = $ ( this ) . parent ( ) . find ( '.comment-form' ) ;
form . removeClass ( 'hide' ) ;
assingMenuAttributes ( form . find ( '.menu' ) ) ;
} ) ;
// The following part is only for diff views
if ( $ ( '.repository.pull.diff' ) . length === 0 ) {
return ;
}
$ ( '.diff-detail-box.ui.sticky' ) . sticky ( ) ;
$ ( '.btn-review' ) . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$ ( this ) . closest ( '.dropdown' ) . find ( '.menu' ) . toggle ( 'visible' ) ;
} ) . closest ( '.dropdown' ) . find ( '.link.close' )
. on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
$ ( this ) . closest ( '.menu' ) . toggle ( 'visible' ) ;
} ) ;
$ ( '.code-view .lines-code,.code-view .lines-num' )
. on ( 'mouseenter' , function ( ) {
const parent = $ ( this ) . closest ( 'td' ) ;
$ ( this ) . closest ( 'tr' ) . addClass (
parent . hasClass ( 'lines-num-old' ) || parent . hasClass ( 'lines-code-old' )
? 'focus-lines-old' : 'focus-lines-new'
) ;
} )
. on ( 'mouseleave' , function ( ) {
$ ( this ) . closest ( 'tr' ) . removeClass ( 'focus-lines-new focus-lines-old' ) ;
} ) ;
$ ( '.add-code-comment' ) . on ( 'click' , function ( e ) {
// https://github.com/go-gitea/gitea/issues/4745
if ( $ ( e . target ) . hasClass ( 'btn-add-single' ) ) {
return ;
}
e . preventDefault ( ) ;
const isSplit = $ ( this ) . closest ( '.code-diff' ) . hasClass ( 'code-diff-split' ) ;
const side = $ ( this ) . data ( 'side' ) ;
const idx = $ ( this ) . data ( 'idx' ) ;
const path = $ ( this ) . data ( 'path' ) ;
const form = $ ( '#pull_review_add_comment' ) . html ( ) ;
const tr = $ ( this ) . closest ( 'tr' ) ;
let ntr = tr . next ( ) ;
if ( ! ntr . hasClass ( 'add-comment' ) ) {
ntr = $ ( ` <tr class="add-comment"> ${
isSplit ? '<td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-right"></td>'
: '<td class="lines-num"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left add-comment-right"></td>'
} < / t r > ` ) ;
tr . after ( ntr ) ;
}
const td = ntr . find ( ` .add-comment- ${ side } ` ) ;
let commentCloud = td . find ( '.comment-code-cloud' ) ;
if ( commentCloud . length === 0 ) {
td . html ( form ) ;
commentCloud = td . find ( '.comment-code-cloud' ) ;
assingMenuAttributes ( commentCloud . find ( '.menu' ) ) ;
td . find ( "input[name='line']" ) . val ( idx ) ;
td . find ( "input[name='side']" ) . val ( side === 'left' ? 'previous' : 'proposed' ) ;
td . find ( "input[name='path']" ) . val ( path ) ;
}
commentCloud . find ( 'textarea' ) . focus ( ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function assingMenuAttributes ( menu ) {
const id = Math . floor ( Math . random ( ) * Math . floor ( 1000000 ) ) ;
menu . attr ( 'data-write' , menu . attr ( 'data-write' ) + id ) ;
menu . attr ( 'data-preview' , menu . attr ( 'data-preview' ) + id ) ;
menu . find ( '.item' ) . each ( function ( ) {
const tab = $ ( this ) . attr ( 'data-tab' ) + id ;
$ ( this ) . attr ( 'data-tab' , tab ) ;
} ) ;
menu . parent ( ) . find ( "*[data-tab='write']" ) . attr ( 'data-tab' , ` write ${ id } ` ) ;
menu . parent ( ) . find ( "*[data-tab='preview']" ) . attr ( 'data-tab' , ` preview ${ id } ` ) ;
initCommentPreviewTab ( menu . parent ( '.form' ) ) ;
return id ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initRepositoryCollaboration ( ) {
// Change collaborator access mode
$ ( '.access-mode.menu .item' ) . click ( function ( ) {
const $menu = $ ( this ) . parent ( ) ;
$ . post ( $menu . data ( 'url' ) , {
_csrf : csrf ,
uid : $menu . data ( 'uid' ) ,
mode : $ ( this ) . data ( 'value' )
} ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initTeamSettings ( ) {
// Change team access mode
$ ( '.organization.new.team input[name=permission]' ) . change ( ( ) => {
const val = $ ( 'input[name=permission]:checked' , '.organization.new.team' ) . val ( ) ;
if ( val === 'admin' ) {
$ ( '.organization.new.team .team-units' ) . hide ( ) ;
} else {
$ ( '.organization.new.team .team-units' ) . show ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initWikiForm ( ) {
const $editArea = $ ( '.repository.wiki textarea#edit_area' ) ;
2019-11-16 05:20:07 +03:00
let sideBySideChanges = 0 ;
let sideBySideTimeout = null ;
2019-11-15 00:39:51 +03:00
if ( $editArea . length > 0 ) {
const simplemde = new SimpleMDE ( {
autoDownloadFontAwesome : false ,
element : $editArea [ 0 ] ,
forceSync : true ,
previewRender ( plainText , preview ) { // Async method
setTimeout ( ( ) => {
// FIXME: still send render request when return back to edit mode
2019-11-16 05:20:07 +03:00
const render = function ( ) {
sideBySideChanges = 0 ;
if ( sideBySideTimeout != null ) {
clearTimeout ( sideBySideTimeout ) ;
sideBySideTimeout = null ;
}
$ . post ( $editArea . data ( 'url' ) , {
_csrf : csrf ,
mode : 'gfm' ,
context : $editArea . data ( 'context' ) ,
text : plainText
} ,
( data ) => {
preview . innerHTML = ` <div class="markdown ui segment"> ${ data } </div> ` ;
emojify . run ( $ ( '.editor-preview' ) [ 0 ] ) ;
$ ( preview ) . find ( 'pre code' ) . each ( ( _ , e ) => {
hljs . highlightBlock ( e ) ;
} ) ;
} ) ;
} ;
if ( ! simplemde . isSideBySideActive ( ) ) {
render ( ) ;
} else {
// delay preview by keystroke counting
sideBySideChanges ++ ;
if ( sideBySideChanges > 10 ) {
render ( ) ;
}
// or delay preview by timeout
if ( sideBySideTimeout != null ) {
clearTimeout ( sideBySideTimeout ) ;
sideBySideTimeout = null ;
}
sideBySideTimeout = setTimeout ( render , 600 ) ;
}
2019-11-15 00:39:51 +03:00
} , 0 ) ;
2019-11-16 05:20:07 +03:00
if ( ! simplemde . isSideBySideActive ( ) ) {
return 'Loading...' ;
}
return preview . innerHTML ;
2019-11-15 00:39:51 +03:00
} ,
renderingConfig : {
singleLineBreaks : false
} ,
indentWithTabs : false ,
tabSize : 4 ,
spellChecker : false ,
toolbar : [ 'bold' , 'italic' , 'strikethrough' , '|' ,
'heading-1' , 'heading-2' , 'heading-3' , 'heading-bigger' , 'heading-smaller' , '|' ,
{
name : 'code-inline' ,
action ( e ) {
const cm = e . codemirror ;
const selection = cm . getSelection ( ) ;
cm . replaceSelection ( ` \` ${ selection } \` ` ) ;
if ( ! selection ) {
const cursorPos = cm . getCursor ( ) ;
cm . setCursor ( cursorPos . line , cursorPos . ch - 1 ) ;
}
cm . focus ( ) ;
} ,
className : 'fa fa-angle-right' ,
title : 'Add Inline Code' ,
} , 'code' , 'quote' , '|' , {
name : 'checkbox-empty' ,
action ( e ) {
const cm = e . codemirror ;
cm . replaceSelection ( ` \n - [ ] ${ cm . getSelection ( ) } ` ) ;
cm . focus ( ) ;
} ,
className : 'fa fa-square-o' ,
title : 'Add Checkbox (empty)' ,
} ,
{
name : 'checkbox-checked' ,
action ( e ) {
const cm = e . codemirror ;
cm . replaceSelection ( ` \n - [x] ${ cm . getSelection ( ) } ` ) ;
cm . focus ( ) ;
} ,
className : 'fa fa-check-square-o' ,
title : 'Add Checkbox (checked)' ,
} , '|' ,
'unordered-list' , 'ordered-list' , '|' ,
'link' , 'image' , 'table' , 'horizontal-rule' , '|' ,
2020-02-01 20:06:57 +03:00
'clean-block' , 'preview' , 'fullscreen' , 'side-by-side' , '|' ,
{
name : 'revert-to-textarea' ,
action ( e ) {
e . toTextArea ( ) ;
} ,
className : 'fa fa-file' ,
title : 'Revert to simple textarea' ,
} ,
]
2019-11-15 00:39:51 +03:00
} ) ;
$ ( simplemde . codemirror . getInputField ( ) ) . addClass ( 'js-quick-submit' ) ;
2019-11-17 22:42:44 +03:00
setTimeout ( ( ) => {
const $bEdit = $ ( '.repository.wiki.new .previewtabs a[data-tab="write"]' ) ;
const $bPrev = $ ( '.repository.wiki.new .previewtabs a[data-tab="preview"]' ) ;
const $toolbar = $ ( '.editor-toolbar' ) ;
const $bPreview = $ ( '.editor-toolbar a.fa-eye' ) ;
const $bSideBySide = $ ( '.editor-toolbar a.fa-columns' ) ;
$bEdit . on ( 'click' , ( ) => {
if ( $toolbar . hasClass ( 'disabled-for-preview' ) ) {
$bPreview . click ( ) ;
}
} ) ;
$bPrev . on ( 'click' , ( ) => {
if ( ! $toolbar . hasClass ( 'disabled-for-preview' ) ) {
$bPreview . click ( ) ;
}
} ) ;
$bPreview . on ( 'click' , ( ) => {
setTimeout ( ( ) => {
if ( $toolbar . hasClass ( 'disabled-for-preview' ) ) {
if ( $bEdit . hasClass ( 'active' ) ) {
$bEdit . removeClass ( 'active' ) ;
}
if ( ! $bPrev . hasClass ( 'active' ) ) {
$bPrev . addClass ( 'active' ) ;
}
} else {
if ( ! $bEdit . hasClass ( 'active' ) ) {
$bEdit . addClass ( 'active' ) ;
}
if ( $bPrev . hasClass ( 'active' ) ) {
$bPrev . removeClass ( 'active' ) ;
}
}
} , 0 ) ;
} ) ;
$bSideBySide . on ( 'click' , ( ) => {
sideBySideChanges = 10 ;
} ) ;
} , 0 ) ;
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
// Adding function to get the cursor position in a text field to jQuery object.
$ . fn . getCursorPosition = function ( ) {
const el = $ ( this ) . 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 ;
} ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function setSimpleMDE ( $editArea ) {
if ( codeMirrorEditor ) {
codeMirrorEditor . toTextArea ( ) ;
codeMirrorEditor = null ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( simpleMDEditor ) {
return true ;
}
simpleMDEditor = new SimpleMDE ( {
autoDownloadFontAwesome : false ,
element : $editArea [ 0 ] ,
forceSync : true ,
renderingConfig : {
singleLineBreaks : false
} ,
indentWithTabs : false ,
tabSize : 4 ,
spellChecker : false ,
previewRender ( plainText , preview ) { // Async method
setTimeout ( ( ) => {
// FIXME: still send render request when return back to edit mode
$ . post ( $editArea . data ( 'url' ) , {
_csrf : csrf ,
mode : 'gfm' ,
context : $editArea . data ( 'context' ) ,
text : plainText
} ,
( data ) => {
preview . innerHTML = ` <div class="markdown ui segment"> ${ data } </div> ` ;
emojify . run ( $ ( '.editor-preview' ) [ 0 ] ) ;
} ) ;
} , 0 ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return 'Loading...' ;
} ,
toolbar : [ 'bold' , 'italic' , 'strikethrough' , '|' ,
'heading-1' , 'heading-2' , 'heading-3' , 'heading-bigger' , 'heading-smaller' , '|' ,
'code' , 'quote' , '|' ,
'unordered-list' , 'ordered-list' , '|' ,
'link' , 'image' , 'table' , 'horizontal-rule' , '|' ,
2020-02-01 20:06:57 +03:00
'clean-block' , 'preview' , 'fullscreen' , 'side-by-side' , '|' ,
{
name : 'revert-to-textarea' ,
action ( e ) {
e . toTextArea ( ) ;
} ,
className : 'fa fa-file' ,
title : 'Revert to simple textarea' ,
} ,
]
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return true ;
}
2019-11-13 17:52:13 +03:00
2019-12-16 18:56:35 +03:00
function setCommentSimpleMDE ( $editArea ) {
const simplemde = new SimpleMDE ( {
autoDownloadFontAwesome : false ,
element : $editArea [ 0 ] ,
forceSync : true ,
renderingConfig : {
singleLineBreaks : false
} ,
indentWithTabs : false ,
tabSize : 4 ,
spellChecker : false ,
toolbar : [ 'bold' , 'italic' , 'strikethrough' , '|' ,
'heading-1' , 'heading-2' , 'heading-3' , 'heading-bigger' , 'heading-smaller' , '|' ,
'code' , 'quote' , '|' ,
'unordered-list' , 'ordered-list' , '|' ,
'link' , 'image' , 'table' , 'horizontal-rule' , '|' ,
2020-02-01 20:06:57 +03:00
'clean-block' , '|' ,
{
name : 'revert-to-textarea' ,
action ( e ) {
e . toTextArea ( ) ;
} ,
className : 'fa fa-file' ,
title : 'Revert to simple textarea' ,
} ,
]
2019-12-16 18:56:35 +03:00
} ) ;
simplemde . codemirror . setOption ( 'extraKeys' , {
Enter : ( ) => {
if ( ! ( issuesTribute . isActive || emojiTribute . isActive ) ) {
return CodeMirror . Pass ;
}
} ,
Backspace : ( cm ) => {
2019-12-30 20:11:50 +03:00
if ( cm . getInputField ( ) . trigger ) {
cm . getInputField ( ) . trigger ( 'input' ) ;
}
2019-12-16 18:56:35 +03:00
cm . execCommand ( 'delCharBefore' ) ;
}
} ) ;
issuesTribute . attach ( simplemde . codemirror . getInputField ( ) ) ;
emojiTribute . attach ( simplemde . codemirror . getInputField ( ) ) ;
return simplemde ;
}
2019-11-15 00:39:51 +03:00
function setCodeMirror ( $editArea ) {
if ( simpleMDEditor ) {
simpleMDEditor . toTextArea ( ) ;
simpleMDEditor = null ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( codeMirrorEditor ) {
return true ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
codeMirrorEditor = CodeMirror . fromTextArea ( $editArea [ 0 ] , {
lineNumbers : true
} ) ;
codeMirrorEditor . on ( 'change' , ( cm , _change ) => {
$editArea . val ( cm . getValue ( ) ) ;
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return true ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function initEditor ( ) {
$ ( '.js-quick-pull-choice-option' ) . change ( function ( ) {
if ( $ ( this ) . val ( ) === 'commit-to-new-branch' ) {
$ ( '.quick-pull-branch-name' ) . show ( ) ;
$ ( '.quick-pull-branch-name input' ) . prop ( 'required' , true ) ;
} else {
$ ( '.quick-pull-branch-name' ) . hide ( ) ;
$ ( '.quick-pull-branch-name input' ) . prop ( 'required' , false ) ;
}
$ ( '#commit-button' ) . text ( $ ( this ) . attr ( 'button_text' ) ) ;
} ) ;
const $editFilename = $ ( '#file-name' ) ;
$editFilename . keyup ( function ( e ) {
const $section = $ ( '.breadcrumb span.section' ) ;
const $divider = $ ( '.breadcrumb div.divider' ) ;
let value ;
let parts ;
if ( e . keyCode === 8 ) {
if ( $ ( this ) . getCursorPosition ( ) === 0 ) {
if ( $section . length > 0 ) {
value = $section . last ( ) . find ( 'a' ) . text ( ) ;
$ ( this ) . val ( value + $ ( this ) . val ( ) ) ;
$ ( this ) [ 0 ] . setSelectionRange ( value . length , value . length ) ;
$section . last ( ) . remove ( ) ;
$divider . last ( ) . remove ( ) ;
}
}
}
if ( e . keyCode === 191 ) {
parts = $ ( this ) . val ( ) . split ( '/' ) ;
for ( let i = 0 ; i < parts . length ; ++ i ) {
value = parts [ i ] ;
if ( i < parts . length - 1 ) {
if ( value . length ) {
$ ( ` <span class="section"><a href="#"> ${ value } </a></span> ` ) . insertBefore ( $ ( this ) ) ;
$ ( '<div class="divider"> / </div>' ) . insertBefore ( $ ( this ) ) ;
}
} else {
$ ( this ) . val ( value ) ;
}
$ ( this ) [ 0 ] . setSelectionRange ( 0 , 0 ) ;
}
}
parts = [ ] ;
$ ( '.breadcrumb span.section' ) . each ( function ( ) {
const element = $ ( this ) ;
if ( element . find ( 'a' ) . length ) {
parts . push ( element . find ( 'a' ) . text ( ) ) ;
} else {
parts . push ( element . text ( ) ) ;
}
} ) ;
if ( $ ( this ) . val ( ) ) parts . push ( $ ( this ) . val ( ) ) ;
$ ( '#tree_path' ) . val ( parts . join ( '/' ) ) ;
} ) . trigger ( 'keyup' ) ;
const $editArea = $ ( '.repository.editor textarea#edit_area' ) ;
if ( ! $editArea . length ) return ;
const markdownFileExts = $editArea . data ( 'markdown-file-exts' ) . split ( ',' ) ;
const lineWrapExtensions = $editArea . data ( 'line-wrap-extensions' ) . split ( ',' ) ;
$editFilename . on ( 'keyup' , ( ) => {
const val = $editFilename . val ( ) ;
let mode , spec , extension , extWithDot , dataUrl , apiCall ;
extension = extWithDot = '' ;
const m = /.+\.([^.]+)$/ . exec ( val ) ;
if ( m ) {
extension = m [ 1 ] ;
extWithDot = ` . ${ extension } ` ;
}
const info = CodeMirror . findModeByExtension ( extension ) ;
const previewLink = $ ( 'a[data-tab=preview]' ) ;
if ( info ) {
mode = info . mode ;
spec = info . mime ;
apiCall = mode ;
} else {
apiCall = extension ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
if ( previewLink . length && apiCall && previewFileModes && previewFileModes . length && previewFileModes . indexOf ( apiCall ) >= 0 ) {
dataUrl = previewLink . data ( 'url' ) ;
previewLink . data ( 'url' , dataUrl . replace ( /(.*)\/.*/i , ` $ 1/ ${ mode } ` ) ) ;
previewLink . show ( ) ;
} else {
previewLink . hide ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
// If this file is a Markdown extensions, we will load that editor and return
if ( markdownFileExts . indexOf ( extWithDot ) >= 0 ) {
if ( setSimpleMDE ( $editArea ) ) {
return ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
// Else we are going to use CodeMirror
if ( ! codeMirrorEditor && ! setCodeMirror ( $editArea ) ) {
return ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
if ( mode ) {
codeMirrorEditor . setOption ( 'mode' , spec ) ;
CodeMirror . autoLoadMode ( codeMirrorEditor , mode ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
if ( lineWrapExtensions . indexOf ( extWithDot ) >= 0 ) {
codeMirrorEditor . setOption ( 'lineWrapping' , true ) ;
} else {
codeMirrorEditor . setOption ( 'lineWrapping' , false ) ;
}
// get the filename without any folder
let value = $editFilename . val ( ) ;
if ( value . length === 0 ) {
return ;
}
value = value . split ( '/' ) ;
value = value [ value . length - 1 ] ;
$ . getJSON ( $editFilename . data ( 'ec-url-prefix' ) + value , ( editorconfig ) => {
if ( editorconfig . indent _style === 'tab' ) {
codeMirrorEditor . setOption ( 'indentWithTabs' , true ) ;
codeMirrorEditor . setOption ( 'extraKeys' , { } ) ;
} else {
codeMirrorEditor . setOption ( 'indentWithTabs' , false ) ;
// required because CodeMirror doesn't seems to use spaces correctly for {"indentWithTabs": false}:
// - https://github.com/codemirror/CodeMirror/issues/988
// - https://codemirror.net/doc/manual.html#keymaps
codeMirrorEditor . setOption ( 'extraKeys' , {
Tab ( cm ) {
const spaces = Array ( parseInt ( cm . getOption ( 'indentUnit' ) ) + 1 ) . join ( ' ' ) ;
cm . replaceSelection ( spaces ) ;
}
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
}
codeMirrorEditor . setOption ( 'indentUnit' , editorconfig . indent _size || 4 ) ;
codeMirrorEditor . setOption ( 'tabSize' , editorconfig . tab _width || 4 ) ;
} ) ;
} ) . trigger ( 'keyup' ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03: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' ;
// Disabling the button at the start
$commitButton . prop ( 'disabled' , true ) ;
// 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 ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$commitButton . 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' ) . submit ( ) ;
}
} ) . modal ( 'show' ) ;
event . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initOrganization ( ) {
if ( $ ( '.organization' ) . length === 0 ) {
return ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
// Options
if ( $ ( '.organization.settings.options' ) . length > 0 ) {
$ ( '#org_name' ) . keyup ( function ( ) {
const $prompt = $ ( '#org-name-change-prompt' ) ;
if ( $ ( this ) . val ( ) . toString ( ) . toLowerCase ( ) !== $ ( this ) . data ( 'org-name' ) . toString ( ) . toLowerCase ( ) ) {
$prompt . show ( ) ;
} else {
$prompt . hide ( ) ;
}
} ) ;
}
}
2019-11-13 17:52:13 +03:00
function initUserSettings ( ) {
2019-11-15 00:39:51 +03:00
// Options
if ( $ ( '.user.settings.profile' ) . length > 0 ) {
$ ( '#username' ) . keyup ( function ( ) {
const $prompt = $ ( '#name-change-prompt' ) ;
if ( $ ( this ) . val ( ) . toString ( ) . toLowerCase ( ) !== $ ( this ) . data ( 'name' ) . toString ( ) . toLowerCase ( ) ) {
$prompt . show ( ) ;
} else {
$prompt . hide ( ) ;
}
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
}
}
2019-11-13 17:52:13 +03:00
2019-11-15 09:56:06 +03:00
function initGithook ( ) {
if ( $ ( '.edit.githook' ) . length === 0 ) {
return ;
}
CodeMirror . autoLoadMode ( CodeMirror . fromTextArea ( $ ( '#content' ) [ 0 ] , {
lineNumbers : true ,
mode : 'shell'
} ) , 'shell' ) ;
}
2019-11-15 00:39:51 +03:00
function initWebhook ( ) {
if ( $ ( '.new.webhook' ) . length === 0 ) {
return ;
}
$ ( '.events.checkbox input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '.events.fields' ) . show ( ) ;
}
} ) ;
$ ( '.non-events.checkbox input' ) . change ( function ( ) {
if ( $ ( this ) . is ( ':checked' ) ) {
$ ( '.events.fields' ) . hide ( ) ;
}
} ) ;
const updateContentType = function ( ) {
const visible = $ ( '#http_method' ) . val ( ) === 'POST' ;
$ ( '#content_type' ) . parent ( ) . parent ( ) [ visible ? 'show' : 'hide' ] ( ) ;
} ;
updateContentType ( ) ;
$ ( '#http_method' ) . change ( ( ) => {
updateContentType ( ) ;
} ) ;
// Test delivery
$ ( '#test-delivery' ) . click ( function ( ) {
const $this = $ ( this ) ;
$this . addClass ( 'loading disabled' ) ;
$ . post ( $this . data ( 'link' ) , {
_csrf : csrf
} ) . done (
setTimeout ( ( ) => {
window . location . href = $this . data ( 'redirect' ) ;
} , 5000 )
) ;
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function initAdmin ( ) {
if ( $ ( '.admin' ) . length === 0 ) {
return ;
}
// New user
if ( $ ( '.admin.new.user' ) . length > 0 || $ ( '.admin.edit.user' ) . length > 0 ) {
$ ( '#login_type' ) . change ( function ( ) {
if ( $ ( this ) . val ( ) . substring ( 0 , 1 ) === '0' ) {
$ ( '#login_name' ) . removeAttr ( 'required' ) ;
$ ( '.non-local' ) . hide ( ) ;
$ ( '.local' ) . show ( ) ;
$ ( '#user_name' ) . focus ( ) ;
if ( $ ( this ) . data ( 'password' ) === 'required' ) {
$ ( '#password' ) . attr ( 'required' , 'required' ) ;
}
} else {
$ ( '#login_name' ) . attr ( 'required' , 'required' ) ;
$ ( '.non-local' ) . show ( ) ;
$ ( '.local' ) . hide ( ) ;
$ ( '#login_name' ) . focus ( ) ;
$ ( '#password' ) . removeAttr ( 'required' ) ;
}
} ) ;
}
function onSecurityProtocolChange ( ) {
if ( $ ( '#security_protocol' ) . val ( ) > 0 ) {
$ ( '.has-tls' ) . show ( ) ;
2019-11-13 17:52:13 +03:00
} else {
2019-11-15 00:39:51 +03:00
$ ( '.has-tls' ) . hide ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
}
function onUsePagedSearchChange ( ) {
if ( $ ( '#use_paged_search' ) . prop ( 'checked' ) ) {
$ ( '.search-page-size' ) . show ( )
. find ( 'input' ) . attr ( 'required' , 'required' ) ;
} else {
$ ( '.search-page-size' ) . hide ( )
. find ( 'input' ) . removeAttr ( 'required' ) ;
}
}
function onOAuth2Change ( ) {
$ ( '.open_id_connect_auto_discovery_url, .oauth2_use_custom_url' ) . hide ( ) ;
$ ( '.open_id_connect_auto_discovery_url input[required]' ) . removeAttr ( 'required' ) ;
const provider = $ ( '#oauth2_provider' ) . val ( ) ;
switch ( provider ) {
case 'github' :
case 'gitlab' :
case 'gitea' :
2020-03-03 06:11:45 +03:00
case 'nextcloud' :
2019-11-15 00:39:51 +03:00
$ ( '.oauth2_use_custom_url' ) . show ( ) ;
break ;
case 'openidConnect' :
$ ( '.open_id_connect_auto_discovery_url input' ) . attr ( 'required' , 'required' ) ;
$ ( '.open_id_connect_auto_discovery_url' ) . show ( ) ;
break ;
}
onOAuth2UseCustomURLChange ( ) ;
}
function onOAuth2UseCustomURLChange ( ) {
const provider = $ ( '#oauth2_provider' ) . val ( ) ;
$ ( '.oauth2_use_custom_url_field' ) . hide ( ) ;
$ ( '.oauth2_use_custom_url_field input[required]' ) . removeAttr ( 'required' ) ;
if ( $ ( '#oauth2_use_custom_url' ) . is ( ':checked' ) ) {
if ( ! $ ( '#oauth2_token_url' ) . val ( ) ) {
$ ( '#oauth2_token_url' ) . val ( $ ( ` # ${ provider } _token_url ` ) . val ( ) ) ;
}
if ( ! $ ( '#oauth2_auth_url' ) . val ( ) ) {
$ ( '#oauth2_auth_url' ) . val ( $ ( ` # ${ provider } _auth_url ` ) . val ( ) ) ;
}
if ( ! $ ( '#oauth2_profile_url' ) . val ( ) ) {
$ ( '#oauth2_profile_url' ) . val ( $ ( ` # ${ provider } _profile_url ` ) . val ( ) ) ;
}
if ( ! $ ( '#oauth2_email_url' ) . val ( ) ) {
$ ( '#oauth2_email_url' ) . val ( $ ( ` # ${ provider } _email_url ` ) . val ( ) ) ;
}
switch ( provider ) {
case 'github' :
$ ( '.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input, .oauth2_email_url input' ) . attr ( 'required' , 'required' ) ;
$ ( '.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url, .oauth2_email_url' ) . show ( ) ;
break ;
2020-03-03 06:11:45 +03:00
case 'nextcloud' :
2019-11-15 00:39:51 +03:00
case 'gitea' :
case 'gitlab' :
$ ( '.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input' ) . attr ( 'required' , 'required' ) ;
$ ( '.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url' ) . show ( ) ;
$ ( '#oauth2_email_url' ) . val ( '' ) ;
break ;
}
}
}
// New authentication
if ( $ ( '.admin.new.authentication' ) . length > 0 ) {
$ ( '#auth_type' ) . change ( function ( ) {
2019-11-23 02:33:31 +03:00
$ ( '.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls .search-page-size .sspi' ) . hide ( ) ;
2019-11-15 00:39:51 +03:00
2019-11-23 02:33:31 +03:00
$ ( '.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]' ) . removeAttr ( 'required' ) ;
2019-11-15 00:39:51 +03:00
$ ( '.binddnrequired' ) . removeClass ( 'required' ) ;
const authType = $ ( this ) . val ( ) ;
switch ( authType ) {
case '2' : // LDAP
$ ( '.ldap' ) . show ( ) ;
$ ( '.binddnrequired input, .ldap div.required:not(.dldap) input' ) . attr ( 'required' , 'required' ) ;
$ ( '.binddnrequired' ) . addClass ( 'required' ) ;
break ;
case '3' : // SMTP
$ ( '.smtp' ) . show ( ) ;
$ ( '.has-tls' ) . show ( ) ;
$ ( '.smtp div.required input, .has-tls' ) . attr ( 'required' , 'required' ) ;
break ;
case '4' : // PAM
$ ( '.pam' ) . show ( ) ;
$ ( '.pam input' ) . attr ( 'required' , 'required' ) ;
break ;
case '5' : // LDAP
$ ( '.dldap' ) . show ( ) ;
$ ( '.dldap div.required:not(.ldap) input' ) . attr ( 'required' , 'required' ) ;
break ;
case '6' : // OAuth2
$ ( '.oauth2' ) . show ( ) ;
$ ( '.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input' ) . attr ( 'required' , 'required' ) ;
onOAuth2Change ( ) ;
break ;
2019-11-23 02:33:31 +03:00
case '7' : // SSPI
$ ( '.sspi' ) . show ( ) ;
$ ( '.sspi div.required input' ) . attr ( 'required' , 'required' ) ;
break ;
2019-11-15 00:39:51 +03:00
}
if ( authType === '2' || authType === '5' ) {
onSecurityProtocolChange ( ) ;
}
if ( authType === '2' ) {
onUsePagedSearchChange ( ) ;
}
} ) ;
$ ( '#auth_type' ) . change ( ) ;
$ ( '#security_protocol' ) . change ( onSecurityProtocolChange ) ;
$ ( '#use_paged_search' ) . change ( onUsePagedSearchChange ) ;
$ ( '#oauth2_provider' ) . change ( onOAuth2Change ) ;
$ ( '#oauth2_use_custom_url' ) . change ( onOAuth2UseCustomURLChange ) ;
}
// Edit authentication
if ( $ ( '.admin.edit.authentication' ) . length > 0 ) {
const authType = $ ( '#auth_type' ) . val ( ) ;
if ( authType === '2' || authType === '5' ) {
$ ( '#security_protocol' ) . change ( onSecurityProtocolChange ) ;
if ( authType === '2' ) {
$ ( '#use_paged_search' ) . change ( onUsePagedSearchChange ) ;
}
} else if ( authType === '6' ) {
$ ( '#oauth2_provider' ) . change ( onOAuth2Change ) ;
$ ( '#oauth2_use_custom_url' ) . change ( onOAuth2UseCustomURLChange ) ;
onOAuth2Change ( ) ;
}
}
// Notice
if ( $ ( '.admin.notice' ) ) {
const $detailModal = $ ( '#detail-modal' ) ;
// Attach view detail modals
$ ( '.view-detail' ) . click ( function ( ) {
$detailModal . find ( '.content p' ) . text ( $ ( this ) . data ( 'content' ) ) ;
$detailModal . modal ( 'show' ) ;
return false ;
} ) ;
// Select actions
const $checkboxes = $ ( '.select.table .ui.checkbox' ) ;
$ ( '.select.action' ) . click ( function ( ) {
switch ( $ ( this ) . data ( 'action' ) ) {
case 'select-all' :
$checkboxes . checkbox ( 'check' ) ;
break ;
case 'deselect-all' :
$checkboxes . checkbox ( 'uncheck' ) ;
break ;
case 'inverse' :
$checkboxes . checkbox ( 'toggle' ) ;
break ;
}
} ) ;
$ ( '#delete-selection' ) . click ( function ( ) {
const $this = $ ( this ) ;
$this . addClass ( 'loading disabled' ) ;
const ids = [ ] ;
$checkboxes . each ( function ( ) {
if ( $ ( this ) . checkbox ( 'is checked' ) ) {
ids . push ( $ ( this ) . data ( 'id' ) ) ;
}
} ) ;
$ . post ( $this . data ( 'link' ) , {
_csrf : csrf ,
ids
} ) . done ( ( ) => {
window . location . href = $this . data ( 'redirect' ) ;
} ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function buttonsClickOnEnter ( ) {
$ ( '.ui.button' ) . keypress ( function ( e ) {
if ( e . keyCode === 13 || e . keyCode === 32 ) { // enter key or space bar
$ ( this ) . click ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function searchUsers ( ) {
const $searchUserBox = $ ( '#search-user-box' ) ;
$searchUserBox . search ( {
minCharacters : 2 ,
apiSettings : {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /api/v1/users/search?q={query} ` ,
2019-11-15 00:39:51 +03:00
onResponse ( response ) {
const items = [ ] ;
$ . each ( response . data , ( _i , item ) => {
let title = item . login ;
if ( item . full _name && item . full _name . length > 0 ) {
title += ` ( ${ htmlEncode ( item . full _name ) } ) ` ;
}
items . push ( {
title ,
image : item . avatar _url
} ) ;
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return { results : items } ;
}
} ,
searchFields : [ 'login' , 'full_name' ] ,
showNoResults : false
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function searchTeams ( ) {
const $searchTeamBox = $ ( '#search-team-box' ) ;
$searchTeamBox . search ( {
minCharacters : 2 ,
apiSettings : {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /api/v1/orgs/ ${ $searchTeamBox . data ( 'org' ) } /teams/search?q={query} ` ,
2019-11-15 00:39:51 +03:00
headers : { 'X-Csrf-Token' : csrf } ,
onResponse ( response ) {
const items = [ ] ;
$ . each ( response . data , ( _i , item ) => {
const title = ` ${ item . name } ( ${ item . permission } access) ` ;
items . push ( {
title ,
} ) ;
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return { results : items } ;
}
} ,
searchFields : [ 'name' , 'description' ] ,
showNoResults : false
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function searchRepositories ( ) {
const $searchRepoBox = $ ( '#search-repo-box' ) ;
$searchRepoBox . search ( {
minCharacters : 2 ,
apiSettings : {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /api/v1/repos/search?q={query}&uid= ${ $searchRepoBox . data ( 'uid' ) } ` ,
2019-11-15 00:39:51 +03:00
onResponse ( response ) {
const items = [ ] ;
$ . each ( response . data , ( _i , item ) => {
items . push ( {
title : item . full _name . split ( '/' ) [ 1 ] ,
description : item . full _name
} ) ;
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return { results : items } ;
}
} ,
searchFields : [ 'full_name' ] ,
showNoResults : false
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function initCodeView ( ) {
if ( $ ( '.code-view .linenums' ) . length > 0 ) {
$ ( document ) . on ( 'click' , '.lines-num span' , function ( e ) {
const $select = $ ( this ) ;
const $list = $select . parent ( ) . siblings ( '.lines-code' ) . find ( 'ol.linenums > li' ) ;
selectRange ( $list , $list . filter ( ` [rel= ${ $select . attr ( 'id' ) } ] ` ) , ( e . shiftKey ? $list . filter ( '.active' ) . eq ( 0 ) : null ) ) ;
deSelect ( ) ;
} ) ;
$ ( window ) . on ( 'hashchange' , ( ) => {
let m = window . location . hash . match ( /^#(L\d+)-(L\d+)$/ ) ;
const $list = $ ( '.code-view ol.linenums > li' ) ;
let $first ;
if ( m ) {
$first = $list . filter ( ` . ${ m [ 1 ] } ` ) ;
selectRange ( $list , $first , $list . filter ( ` . ${ m [ 2 ] } ` ) ) ;
$ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
return ;
}
m = window . location . hash . match ( /^#(L|n)(\d+)$/ ) ;
if ( m ) {
$first = $list . filter ( ` .L ${ m [ 2 ] } ` ) ;
selectRange ( $list , $first ) ;
$ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
}
} ) . trigger ( 'hashchange' ) ;
}
2020-01-26 11:17:25 +03:00
$ ( '.fold-code' ) . on ( 'click' , ( { target } ) => {
const box = target . closest ( '.file-content' ) ;
const folded = box . dataset . folded !== 'true' ;
target . classList . add ( ` fa-chevron- ${ folded ? 'right' : 'down' } ` ) ;
target . classList . remove ( ` fa-chevron- ${ folded ? 'down' : 'right' } ` ) ;
box . dataset . folded = String ( folded ) ;
2019-11-15 05:52:59 +03:00
} ) ;
function insertBlobExcerpt ( e ) {
const $blob = $ ( e . target ) ;
const $row = $blob . parent ( ) . parent ( ) ;
$ . get ( ` ${ $blob . data ( 'url' ) } ? ${ $blob . data ( 'query' ) } &anchor= ${ $blob . data ( 'anchor' ) } ` , ( blob ) => {
$row . replaceWith ( blob ) ;
$ ( ` [data-anchor=" ${ $blob . data ( 'anchor' ) } "] ` ) . on ( 'click' , ( e ) => { insertBlobExcerpt ( e ) ; } ) ;
2019-12-07 21:01:57 +03:00
$ ( '.diff-detail-box.ui.sticky' ) . sticky ( ) ;
2019-11-15 05:52:59 +03:00
} ) ;
}
$ ( '.ui.blob-excerpt' ) . on ( 'click' , ( e ) => { insertBlobExcerpt ( e ) ; } ) ;
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initU2FAuth ( ) {
if ( $ ( '#wait-for-key' ) . length === 0 ) {
return ;
}
u2fApi . ensureSupport ( )
. then ( ( ) => {
2020-02-12 04:53:18 +03:00
$ . getJSON ( ` ${ AppSubUrl } /user/u2f/challenge ` ) . success ( ( req ) => {
2019-11-15 00:39:51 +03:00
u2fApi . sign ( req . appId , req . challenge , req . registeredKeys , 30 )
. then ( u2fSigned )
. catch ( ( err ) => {
if ( err === undefined ) {
u2fError ( 1 ) ;
return ;
}
u2fError ( err . metaData . code ) ;
} ) ;
} ) ;
} ) . catch ( ( ) => {
// Fallback in case browser do not support U2F
2020-02-12 04:53:18 +03:00
window . location . href = ` ${ AppSubUrl } /user/two_factor ` ;
2019-11-15 00:39:51 +03:00
} ) ;
}
function u2fSigned ( resp ) {
$ . ajax ( {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /user/u2f/sign ` ,
2019-11-15 00:39:51 +03:00
type : 'POST' ,
headers : { 'X-Csrf-Token' : csrf } ,
data : JSON . stringify ( resp ) ,
contentType : 'application/json; charset=utf-8' ,
} ) . done ( ( res ) => {
window . location . replace ( res ) ;
} ) . fail ( ( ) => {
u2fError ( 1 ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function u2fRegistered ( resp ) {
if ( checkError ( resp ) ) {
return ;
}
$ . ajax ( {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /user/settings/security/u2f/register ` ,
2019-11-15 00:39:51 +03:00
type : 'POST' ,
headers : { 'X-Csrf-Token' : csrf } ,
data : JSON . stringify ( resp ) ,
contentType : 'application/json; charset=utf-8' ,
success ( ) {
reload ( ) ;
} ,
fail ( ) {
u2fError ( 1 ) ;
}
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function checkError ( resp ) {
if ( ! ( 'errorCode' in resp ) ) {
return false ;
}
if ( resp . errorCode === 0 ) {
2019-11-13 17:52:13 +03:00
return false ;
2019-11-15 00:39:51 +03:00
}
u2fError ( resp . errorCode ) ;
return true ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function u2fError ( errorType ) {
const u2fErrors = {
browser : $ ( '#unsupported-browser' ) ,
1 : $ ( '#u2f-error-1' ) ,
2 : $ ( '#u2f-error-2' ) ,
3 : $ ( '#u2f-error-3' ) ,
4 : $ ( '#u2f-error-4' ) ,
5 : $ ( '.u2f-error-5' )
} ;
u2fErrors [ errorType ] . removeClass ( 'hide' ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
Object . keys ( u2fErrors ) . forEach ( ( type ) => {
if ( type !== errorType ) {
u2fErrors [ type ] . addClass ( 'hide' ) ;
}
} ) ;
$ ( '#u2f-error' ) . modal ( 'show' ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initU2FRegister ( ) {
$ ( '#register-device' ) . modal ( { allowMultiple : false } ) ;
$ ( '#u2f-error' ) . modal ( { allowMultiple : false } ) ;
$ ( '#register-security-key' ) . on ( 'click' , ( e ) => {
e . preventDefault ( ) ;
u2fApi . ensureSupport ( )
. then ( u2fRegisterRequest )
. catch ( ( ) => {
u2fError ( 'browser' ) ;
} ) ;
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function u2fRegisterRequest ( ) {
2020-02-12 04:53:18 +03:00
$ . post ( ` ${ AppSubUrl } /user/settings/security/u2f/request_register ` , {
2019-11-15 00:39:51 +03:00
_csrf : csrf ,
name : $ ( '#nickname' ) . val ( )
} ) . success ( ( req ) => {
$ ( '#nickname' ) . closest ( 'div.field' ) . removeClass ( 'error' ) ;
$ ( '#register-device' ) . modal ( 'show' ) ;
if ( req . registeredKeys === null ) {
req . registeredKeys = [ ] ;
}
u2fApi . register ( req . appId , req . registerRequests , req . registeredKeys , 30 )
. then ( u2fRegistered )
. catch ( ( reason ) => {
if ( reason === undefined ) {
u2fError ( 1 ) ;
return ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
u2fError ( reason . metaData . code ) ;
} ) ;
} ) . fail ( ( xhr ) => {
if ( xhr . status === 409 ) {
$ ( '#nickname' ) . closest ( 'div.field' ) . addClass ( 'error' ) ;
}
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function initWipTitle ( ) {
$ ( '.title_wip_desc > a' ) . click ( ( e ) => {
e . preventDefault ( ) ;
const $issueTitle = $ ( '#issue_title' ) ;
$issueTitle . focus ( ) ;
const value = $issueTitle . val ( ) . trim ( ) . toUpperCase ( ) ;
for ( const i in wipPrefixes ) {
if ( value . startsWith ( wipPrefixes [ i ] . toUpperCase ( ) ) ) {
2019-11-13 17:52:13 +03:00
return ;
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
$issueTitle . val ( ` ${ wipPrefixes [ 0 ] } ${ $issueTitle . val ( ) } ` ) ;
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initTemplateSearch ( ) {
const $repoTemplate = $ ( '#repo_template' ) ;
const checkTemplate = function ( ) {
const $templateUnits = $ ( '#template_units' ) ;
const $nonTemplate = $ ( '#non_template' ) ;
2020-01-05 05:39:14 +03:00
if ( $repoTemplate . val ( ) !== '' && $repoTemplate . val ( ) !== '0' ) {
2019-11-15 00:39:51 +03:00
$templateUnits . show ( ) ;
$nonTemplate . hide ( ) ;
} else {
$templateUnits . hide ( ) ;
$nonTemplate . show ( ) ;
}
} ;
$repoTemplate . change ( checkTemplate ) ;
checkTemplate ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const changeOwner = function ( ) {
$ ( '#repo_template_search' )
. dropdown ( {
apiSettings : {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /api/v1/repos/search?q={query}&template=true&priority_owner_id= ${ $ ( '#uid' ) . val ( ) } ` ,
2019-11-15 00:39:51 +03:00
onResponse ( response ) {
const filteredResponse = { success : true , results : [ ] } ;
filteredResponse . results . push ( {
name : '' ,
value : ''
} ) ;
// Parse the response from the api to work with our dropdown
$ . each ( response . data , ( _r , repo ) => {
filteredResponse . results . push ( {
name : htmlEncode ( repo . full _name ) ,
value : repo . id
} ) ;
} ) ;
return filteredResponse ;
} ,
cache : false ,
2019-11-13 17:52:13 +03:00
} ,
2019-11-15 00:39:51 +03:00
fullTextSearch : true
} ) ;
} ;
$ ( '#uid' ) . change ( changeOwner ) ;
changeOwner ( ) ;
}
2020-01-29 00:57:20 +03:00
$ ( document ) . ready ( async ( ) => {
2019-11-15 00:39:51 +03:00
csrf = $ ( 'meta[name=_csrf]' ) . attr ( 'content' ) ;
// Show exact time
$ ( '.time-since' ) . each ( function ( ) {
$ ( this )
. addClass ( 'poping up' )
. attr ( 'data-content' , $ ( this ) . attr ( 'title' ) )
. attr ( 'data-variation' , 'inverted tiny' )
. attr ( 'title' , '' ) ;
} ) ;
// Semantic UI modules.
$ ( '.dropdown:not(.custom)' ) . dropdown ( ) ;
$ ( '.jump.dropdown' ) . dropdown ( {
action : 'hide' ,
onShow ( ) {
$ ( '.poping.up' ) . popup ( 'hide' ) ;
}
} ) ;
$ ( '.slide.up.dropdown' ) . dropdown ( {
transition : 'slide up'
} ) ;
$ ( '.upward.dropdown' ) . dropdown ( {
direction : 'upward'
} ) ;
$ ( '.ui.accordion' ) . accordion ( ) ;
$ ( '.ui.checkbox' ) . checkbox ( ) ;
$ ( '.ui.progress' ) . progress ( {
showActivity : false
} ) ;
$ ( '.poping.up' ) . popup ( ) ;
$ ( '.top.menu .poping.up' ) . popup ( {
onShow ( ) {
if ( $ ( '.top.menu .menu.transition' ) . hasClass ( 'visible' ) ) {
return false ;
}
}
} ) ;
$ ( '.tabular.menu .item' ) . tab ( ) ;
$ ( '.tabable.menu .item' ) . tab ( ) ;
$ ( '.toggle.button' ) . click ( function ( ) {
$ ( $ ( this ) . data ( 'target' ) ) . slideToggle ( 100 ) ;
} ) ;
// make table <tr> element clickable like a link
$ ( 'tr[data-href]' ) . click ( function ( ) {
window . location = $ ( this ) . data ( 'href' ) ;
} ) ;
// Dropzone
const $dropzone = $ ( '#dropzone' ) ;
if ( $dropzone . length > 0 ) {
const filenameDict = { } ;
new Dropzone ( '#dropzone' , {
url : $dropzone . data ( 'upload-url' ) ,
headers : { 'X-Csrf-Token' : csrf } ,
maxFiles : $dropzone . data ( 'max-file' ) ,
maxFilesize : $dropzone . data ( 'max-size' ) ,
acceptedFiles : ( $dropzone . data ( 'accepts' ) === '*/*' ) ? null : $dropzone . data ( 'accepts' ) ,
addRemoveLinks : true ,
dictDefaultMessage : $dropzone . data ( 'default-message' ) ,
dictInvalidFileType : $dropzone . data ( 'invalid-input-type' ) ,
dictFileTooBig : $dropzone . data ( 'file-too-big' ) ,
dictRemoveFile : $dropzone . data ( 'remove-file' ) ,
init ( ) {
this . on ( 'success' , ( file , data ) => {
filenameDict [ file . name ] = data . uuid ;
const input = $ ( ` <input id=" ${ data . uuid } " name="files" type="hidden"> ` ) . val ( data . uuid ) ;
$ ( '.files' ) . append ( input ) ;
} ) ;
this . on ( 'removedfile' , ( file ) => {
if ( file . name in filenameDict ) {
$ ( ` # ${ filenameDict [ file . name ] } ` ) . remove ( ) ;
}
if ( $dropzone . data ( 'remove-url' ) && $dropzone . data ( 'csrf' ) ) {
$ . post ( $dropzone . data ( 'remove-url' ) , {
file : filenameDict [ file . name ] ,
_csrf : $dropzone . data ( 'csrf' )
} ) ;
}
} ) ;
} ,
} ) ;
}
// Emojify
emojify . setConfig ( {
2020-02-12 04:53:18 +03:00
img _dir : ` ${ AppSubUrl } /vendor/plugins/emojify/images ` ,
2019-11-15 00:39:51 +03:00
ignore _emoticons : true
} ) ;
const hasEmoji = document . getElementsByClassName ( 'has-emoji' ) ;
for ( let i = 0 ; i < hasEmoji . length ; i ++ ) {
emojify . run ( hasEmoji [ i ] ) ;
for ( let j = 0 ; j < hasEmoji [ i ] . childNodes . length ; j ++ ) {
if ( hasEmoji [ i ] . childNodes [ j ] . nodeName === 'A' ) {
emojify . run ( hasEmoji [ i ] . childNodes [ j ] ) ;
}
}
}
// Helpers.
$ ( '.delete-button' ) . click ( showDeletePopup ) ;
$ ( '.add-all-button' ) . click ( showAddAllPopup ) ;
2020-02-25 23:28:47 +03:00
$ ( '.link-action' ) . click ( linkAction ) ;
2020-03-02 21:25:36 +03:00
$ ( '.link-email-action' ) . click ( linkEmailAction ) ;
2019-11-15 00:39:51 +03:00
$ ( '.delete-branch-button' ) . click ( showDeletePopup ) ;
$ ( '.undo-button' ) . click ( function ( ) {
const $this = $ ( this ) ;
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
id : $this . data ( 'id' )
} ) . done ( ( data ) => {
window . location . href = data . redirect ;
} ) ;
} ) ;
$ ( '.show-panel.button' ) . click ( function ( ) {
$ ( $ ( this ) . data ( 'panel' ) ) . show ( ) ;
} ) ;
$ ( '.show-modal.button' ) . click ( function ( ) {
$ ( $ ( this ) . data ( 'modal' ) ) . modal ( 'show' ) ;
} ) ;
$ ( '.delete-post.button' ) . click ( function ( ) {
const $this = $ ( this ) ;
$ . post ( $this . data ( 'request-url' ) , {
_csrf : csrf
} ) . done ( ( ) => {
window . location . href = $this . data ( 'done-url' ) ;
} ) ;
} ) ;
// Set anchor.
$ ( '.markdown' ) . each ( function ( ) {
$ ( this ) . find ( 'h1, h2, h3, h4, h5, h6' ) . each ( function ( ) {
let node = $ ( this ) ;
2019-12-24 01:38:50 +03:00
node = node . wrap ( '<div class="anchor-wrap"></div>' ) ;
2020-02-11 20:02:41 +03:00
node . append ( ` <a class="anchor" href="# ${ encodeURIComponent ( node . attr ( 'id' ) ) } "> ${ svg ( 'octicon-link' , 16 ) } </a> ` ) ;
2019-11-15 00:39:51 +03:00
} ) ;
} ) ;
$ ( '.issue-checkbox' ) . click ( ( ) => {
const numChecked = $ ( '.issue-checkbox' ) . children ( 'input:checked' ) . length ;
if ( numChecked > 0 ) {
$ ( '#issue-filters' ) . addClass ( 'hide' ) ;
$ ( '#issue-actions' ) . removeClass ( 'hide' ) ;
} else {
$ ( '#issue-filters' ) . removeClass ( 'hide' ) ;
$ ( '#issue-actions' ) . addClass ( 'hide' ) ;
}
} ) ;
$ ( '.issue-action' ) . click ( function ( ) {
let { action } = this . dataset ;
let { elementId } = this . dataset ;
const issueIDs = $ ( '.issue-checkbox' ) . children ( 'input:checked' ) . map ( function ( ) {
return this . dataset . issueId ;
} ) . get ( ) . join ( ) ;
const { url } = this . dataset ;
if ( elementId === '0' && url . substr ( - 9 ) === '/assignee' ) {
elementId = '' ;
action = 'clear' ;
}
updateIssuesMeta ( url , action , issueIDs , elementId ) . then ( ( ) => {
// NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the checkboxes stay checked after reload
if ( action === 'close' || action === 'open' ) {
// uncheck all checkboxes
$ ( '.issue-checkbox input[type="checkbox"]' ) . each ( ( _ , e ) => { e . checked = false ; } ) ;
}
reload ( ) ;
} ) ;
} ) ;
// NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay checked after reload
// trigger ckecked event, if checkboxes are checked on load
$ ( '.issue-checkbox input[type="checkbox"]:checked' ) . first ( ) . each ( ( _ , e ) => {
e . checked = false ;
$ ( e ) . click ( ) ;
} ) ;
buttonsClickOnEnter ( ) ;
searchUsers ( ) ;
searchTeams ( ) ;
searchRepositories ( ) ;
initCommentForm ( ) ;
initInstall ( ) ;
initRepository ( ) ;
initMigration ( ) ;
initWikiForm ( ) ;
initEditForm ( ) ;
initEditor ( ) ;
initOrganization ( ) ;
2019-11-15 09:56:06 +03:00
initGithook ( ) ;
2019-11-15 00:39:51 +03:00
initWebhook ( ) ;
initAdmin ( ) ;
initCodeView ( ) ;
initVueApp ( ) ;
initTeamSettings ( ) ;
initCtrlEnterSubmit ( ) ;
initNavbarContentToggle ( ) ;
initTopicbar ( ) ;
initU2FAuth ( ) ;
initU2FRegister ( ) ;
initIssueList ( ) ;
initWipTitle ( ) ;
initPullRequestReview ( ) ;
initRepoStatusChecker ( ) ;
initTemplateSearch ( ) ;
2020-02-12 04:53:18 +03:00
initContextPopups ( ) ;
2019-11-15 00:39:51 +03:00
// Repo clone url.
if ( $ ( '#repo-clone-url' ) . length > 0 ) {
switch ( localStorage . getItem ( 'repo-clone-protocol' ) ) {
case 'ssh' :
if ( $ ( '#repo-clone-ssh' ) . click ( ) . length === 0 ) {
$ ( '#repo-clone-https' ) . click ( ) ;
}
break ;
default :
$ ( '#repo-clone-https' ) . click ( ) ;
break ;
}
}
const routes = {
'div.user.settings' : initUserSettings ,
'div.repository.settings.collaboration' : initRepositoryCollaboration
} ;
let selector ;
for ( selector in routes ) {
if ( $ ( selector ) . length > 0 ) {
routes [ selector ] ( ) ;
break ;
}
}
const $cloneAddr = $ ( '#clone_addr' ) ;
$cloneAddr . change ( ( ) => {
const $repoName = $ ( '#repo_name' ) ;
if ( $cloneAddr . val ( ) . length > 0 && $repoName . val ( ) . length === 0 ) { // Only modify if repo_name input is blank
$repoName . val ( $cloneAddr . val ( ) . match ( /^(.*\/)?((.+?)(\.git)?)$/ ) [ 3 ] ) ;
}
} ) ;
2020-01-29 00:57:20 +03:00
2020-02-08 02:03:42 +03:00
// parallel init of lazy-loaded features
2020-01-29 00:57:20 +03:00
[ hljs ] = await Promise . all ( [
initHighlight ( ) ,
2020-02-08 02:03:42 +03:00
initGitGraph ( ) ,
initClipboard ( ) ,
2020-02-24 00:34:28 +03:00
initUserHeatmap ( ) ,
2020-01-29 00:57:20 +03:00
] ) ;
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function changeHash ( hash ) {
if ( window . history . pushState ) {
window . history . pushState ( null , null , hash ) ;
} else {
window . location . hash = hash ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function deSelect ( ) {
if ( window . getSelection ) {
window . getSelection ( ) . removeAllRanges ( ) ;
} else {
document . selection . empty ( ) ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function selectRange ( $list , $select , $from ) {
$list . removeClass ( 'active' ) ;
if ( $from ) {
let a = parseInt ( $select . attr ( 'rel' ) . substr ( 1 ) ) ;
let b = parseInt ( $from . attr ( 'rel' ) . substr ( 1 ) ) ;
let c ;
if ( a !== b ) {
if ( a > b ) {
c = a ;
a = b ;
b = c ;
}
const classes = [ ] ;
for ( let i = a ; i <= b ; i ++ ) {
classes . push ( ` .L ${ i } ` ) ;
}
$list . filter ( classes . join ( ',' ) ) . addClass ( 'active' ) ;
changeHash ( ` #L ${ a } -L ${ b } ` ) ;
return ;
}
}
$select . addClass ( 'active' ) ;
changeHash ( ` # ${ $select . attr ( 'rel' ) } ` ) ;
}
$ ( ( ) => {
// Warn users that try to leave a page after entering data into a form.
// Except on sign-in pages, and for forms marked as 'ignore-dirty'.
if ( $ ( '.user.signin' ) . length === 0 ) {
$ ( 'form:not(.ignore-dirty)' ) . areYouSure ( ) ;
}
// Parse SSH Key
$ ( '#ssh-key-content' ) . on ( 'change paste keyup' , function ( ) {
const arrays = $ ( this ) . val ( ) . split ( ' ' ) ;
const $title = $ ( '#ssh-key-title' ) ;
if ( $title . val ( ) === '' && arrays . length === 3 && arrays [ 2 ] !== '' ) {
$title . val ( arrays [ 2 ] ) ;
}
} ) ;
} ) ;
function showDeletePopup ( ) {
const $this = $ ( this ) ;
let filter = '' ;
if ( $this . attr ( 'id' ) ) {
filter += ` # ${ $this . attr ( 'id' ) } ` ;
}
const dialog = $ ( ` .delete.modal ${ filter } ` ) ;
dialog . find ( '.name' ) . text ( $this . data ( 'name' ) ) ;
dialog . modal ( {
closable : false ,
onApprove ( ) {
if ( $this . data ( 'type' ) === 'form' ) {
$ ( $this . data ( 'form' ) ) . submit ( ) ;
2019-11-13 17:52:13 +03:00
return ;
2019-11-15 00:39:51 +03:00
}
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
id : $this . data ( 'id' )
} ) . done ( ( data ) => {
window . location . href = data . redirect ;
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) . modal ( 'show' ) ;
return false ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function showAddAllPopup ( ) {
const $this = $ ( this ) ;
let filter = '' ;
if ( $this . attr ( 'id' ) ) {
filter += ` # ${ $this . attr ( 'id' ) } ` ;
}
const dialog = $ ( ` .addall.modal ${ filter } ` ) ;
dialog . find ( '.name' ) . text ( $this . data ( 'name' ) ) ;
dialog . modal ( {
closable : false ,
onApprove ( ) {
if ( $this . data ( 'type' ) === 'form' ) {
$ ( $this . data ( 'form' ) ) . submit ( ) ;
return ;
}
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf ,
id : $this . data ( 'id' )
} ) . done ( ( data ) => {
window . location . href = data . redirect ;
} ) ;
}
} ) . modal ( 'show' ) ;
return false ;
}
2020-02-25 23:28:47 +03:00
function linkAction ( ) {
const $this = $ ( this ) ;
2020-03-03 07:50:31 +03:00
const redirect = $this . data ( 'redirect' ) ;
2020-02-25 23:28:47 +03:00
$ . post ( $this . data ( 'url' ) , {
_csrf : csrf
} ) . done ( ( data ) => {
if ( data . redirect ) {
window . location . href = data . redirect ;
2020-03-03 07:50:31 +03:00
} else if ( redirect ) {
window . location . href = redirect ;
2020-02-25 23:28:47 +03:00
} else {
window . location . reload ( ) ;
}
} ) ;
}
2020-03-02 21:25:36 +03:00
function linkEmailAction ( e ) {
const $this = $ ( this ) ;
$ ( '#form-uid' ) . val ( $this . data ( 'uid' ) ) ;
$ ( '#form-email' ) . val ( $this . data ( 'email' ) ) ;
$ ( '#form-primary' ) . val ( $this . data ( 'primary' ) ) ;
$ ( '#form-activate' ) . val ( $this . data ( 'activate' ) ) ;
$ ( '#form-uid' ) . val ( $this . data ( 'uid' ) ) ;
$ ( '#change-email-modal' ) . modal ( 'show' ) ;
e . preventDefault ( ) ;
}
2019-11-15 00:39:51 +03:00
function initVueComponents ( ) {
const vueDelimeters = [ '${' , '}' ] ;
Vue . component ( 'repo-search' , {
delimiters : vueDelimeters ,
props : {
searchLimit : {
type : Number ,
default : 10
} ,
suburl : {
type : String ,
required : true
} ,
uid : {
type : Number ,
required : true
} ,
organizations : {
type : Array ,
default : [ ]
} ,
isOrganization : {
type : Boolean ,
default : true
} ,
canCreateOrganization : {
type : Boolean ,
default : false
} ,
organizationsTotalCount : {
type : Number ,
default : 0
} ,
moreReposLink : {
type : String ,
default : ''
}
} ,
data ( ) {
return {
tab : 'repos' ,
repos : [ ] ,
reposTotalCount : 0 ,
reposFilter : 'all' ,
searchQuery : '' ,
isLoading : false ,
2020-02-12 04:53:18 +03:00
staticPrefix : StaticUrlPrefix ,
2019-11-15 00:39:51 +03:00
repoTypes : {
all : {
count : 0 ,
searchMode : '' ,
} ,
forks : {
count : 0 ,
searchMode : 'fork' ,
} ,
mirrors : {
count : 0 ,
searchMode : 'mirror' ,
} ,
sources : {
count : 0 ,
searchMode : 'source' ,
} ,
collaborative : {
count : 0 ,
searchMode : 'collaborative' ,
} ,
}
} ;
} ,
computed : {
showMoreReposLink ( ) {
return this . repos . length > 0 && this . repos . length < this . repoTypes [ this . reposFilter ] . count ;
} ,
searchURL ( ) {
return ` ${ this . suburl } /api/v1/repos/search?sort=updated&order=desc&uid= ${ this . uid } &q= ${ this . searchQuery
} & limit = $ { this . searchLimit } & mode = $ { this . repoTypes [ this . reposFilter ] . searchMode
} $ { this . reposFilter !== 'all' ? '&exclusive=1' : '' } ` ;
} ,
repoTypeCount ( ) {
return this . repoTypes [ this . reposFilter ] . count ;
}
} ,
mounted ( ) {
this . searchRepos ( this . reposFilter ) ;
const self = this ;
Vue . nextTick ( ( ) => {
self . $refs . search . focus ( ) ;
} ) ;
} ,
methods : {
changeTab ( t ) {
this . tab = t ;
} ,
changeReposFilter ( filter ) {
this . reposFilter = filter ;
this . repos = [ ] ;
this . repoTypes [ filter ] . count = 0 ;
this . searchRepos ( filter ) ;
} ,
showRepo ( repo , filter ) {
switch ( filter ) {
case 'sources' :
return repo . owner . id === this . uid && ! repo . mirror && ! repo . fork ;
case 'forks' :
return repo . owner . id === this . uid && ! repo . mirror && repo . fork ;
case 'mirrors' :
return repo . mirror ;
case 'collaborative' :
return repo . owner . id !== this . uid && ! repo . mirror ;
default :
return true ;
}
} ,
searchRepos ( reposFilter ) {
const self = this ;
this . isLoading = true ;
const searchedMode = this . repoTypes [ reposFilter ] . searchMode ;
const searchedURL = this . searchURL ;
const searchedQuery = this . searchQuery ;
$ . getJSON ( searchedURL , ( result , _textStatus , request ) => {
if ( searchedURL === self . searchURL ) {
self . repos = result . data ;
const count = request . getResponseHeader ( 'X-Total-Count' ) ;
if ( searchedQuery === '' && searchedMode === '' ) {
self . reposTotalCount = count ;
}
self . repoTypes [ reposFilter ] . count = count ;
}
} ) . always ( ( ) => {
if ( searchedURL === self . searchURL ) {
self . isLoading = false ;
}
} ) ;
} ,
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
repoClass ( repo ) {
if ( repo . fork ) {
2020-02-11 20:02:41 +03:00
return 'octicon-repo-forked' ;
2019-11-15 00:39:51 +03:00
} if ( repo . mirror ) {
2020-02-11 20:02:41 +03:00
return 'octicon-repo-clone' ;
2020-02-12 04:53:18 +03:00
} if ( repo . template ) {
return ` octicon-repo-template ${ repo . private ? '-private' : '' } ` ;
2019-11-15 00:39:51 +03:00
} if ( repo . private ) {
2020-02-11 20:02:41 +03:00
return 'octicon-lock' ;
2019-11-15 00:39:51 +03:00
}
2020-02-11 20:02:41 +03:00
return 'octicon-repo' ;
2019-11-15 00:39:51 +03:00
}
}
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initCtrlEnterSubmit ( ) {
$ ( '.js-quick-submit' ) . keydown ( function ( e ) {
if ( ( ( e . ctrlKey && ! e . altKey ) || e . metaKey ) && ( e . keyCode === 13 || e . keyCode === 10 ) ) {
$ ( this ) . closest ( 'form' ) . submit ( ) ;
}
} ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initVueApp ( ) {
const el = document . getElementById ( 'app' ) ;
if ( ! el ) {
return ;
}
initVueComponents ( ) ;
new Vue ( {
delimiters : [ '${' , '}' ] ,
el ,
data : {
2020-02-24 00:34:28 +03:00
searchLimit : Number ( ( document . querySelector ( 'meta[name=_search_limit]' ) || { } ) . content ) ,
2020-02-12 04:53:18 +03:00
suburl : AppSubUrl ,
2020-01-20 13:07:30 +03:00
uid : Number ( ( document . querySelector ( 'meta[name=_context_uid]' ) || { } ) . content ) ,
activityTopAuthors : window . ActivityTopAuthors || [ ] ,
} ,
components : {
ActivityTopAuthors ,
2019-11-15 00:39:51 +03:00
} ,
} ) ;
}
window . timeAddManual = function ( ) {
$ ( '.mini.modal' )
. modal ( {
duration : 200 ,
onApprove ( ) {
$ ( '#add_time_manual_form' ) . submit ( ) ;
}
} ) . modal ( 'show' ) ;
} ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
window . toggleStopwatch = function ( ) {
$ ( '#toggle_stopwatch_form' ) . submit ( ) ;
} ;
window . cancelStopwatch = function ( ) {
$ ( '#cancel_stopwatch_form' ) . submit ( ) ;
} ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
function initFilterBranchTagDropdown ( selector ) {
$ ( selector ) . each ( function ( ) {
const $dropdown = $ ( this ) ;
const $data = $dropdown . find ( '.data' ) ;
const data = {
items : [ ] ,
mode : $data . data ( 'mode' ) ,
searchTerm : '' ,
noResults : '' ,
canCreateBranch : false ,
menuVisible : false ,
active : 0
} ;
$data . find ( '.item' ) . each ( function ( ) {
data . items . push ( {
name : $ ( this ) . text ( ) ,
url : $ ( this ) . data ( 'url' ) ,
branch : $ ( this ) . hasClass ( 'branch' ) ,
tag : $ ( this ) . hasClass ( 'tag' ) ,
selected : $ ( this ) . hasClass ( 'selected' )
} ) ;
} ) ;
$data . remove ( ) ;
2019-11-13 17:52:13 +03:00
new Vue ( {
2019-11-15 00:39:51 +03:00
delimiters : [ '${' , '}' ] ,
el : this ,
data ,
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
beforeMount ( ) {
const vm = this ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
this . noResults = vm . $el . getAttribute ( 'data-no-results' ) ;
this . canCreateBranch = vm . $el . getAttribute ( 'data-can-create-branch' ) === 'true' ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
document . body . addEventListener ( 'click' , ( event ) => {
if ( vm . $el . contains ( event . target ) ) {
return ;
}
if ( vm . menuVisible ) {
Vue . set ( vm , 'menuVisible' , false ) ;
}
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ,
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
watch : {
menuVisible ( visible ) {
if ( visible ) {
this . focusSearchField ( ) ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ,
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
computed : {
filteredItems ( ) {
const vm = this ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const items = vm . items . filter ( ( item ) => {
return ( ( vm . mode === 'branches' && item . branch ) || ( vm . mode === 'tags' && item . tag ) )
&& ( ! vm . searchTerm || item . name . toLowerCase ( ) . indexOf ( vm . searchTerm . toLowerCase ( ) ) >= 0 ) ;
} ) ;
vm . active = ( items . length === 0 && vm . showCreateNewBranch ? 0 : - 1 ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return items ;
2019-11-13 17:52:13 +03:00
} ,
2019-11-15 00:39:51 +03:00
showNoResults ( ) {
return this . filteredItems . length === 0 && ! this . showCreateNewBranch ;
2019-11-13 17:52:13 +03:00
} ,
2019-11-15 00:39:51 +03:00
showCreateNewBranch ( ) {
const vm = this ;
if ( ! this . canCreateBranch || ! vm . searchTerm || vm . mode === 'tags' ) {
return false ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
return vm . items . filter ( ( item ) => item . name . toLowerCase ( ) === vm . searchTerm . toLowerCase ( ) ) . length === 0 ;
}
} ,
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
methods : {
selectItem ( item ) {
const prev = this . getSelected ( ) ;
if ( prev !== null ) {
prev . selected = false ;
}
item . selected = true ;
window . location . href = item . url ;
2019-11-13 17:52:13 +03:00
} ,
2019-11-15 00:39:51 +03:00
createNewBranch ( ) {
if ( ! this . showCreateNewBranch ) {
return ;
}
this . $refs . newBranchForm . submit ( ) ;
2019-11-13 17:52:13 +03:00
} ,
2019-11-15 00:39:51 +03:00
focusSearchField ( ) {
const vm = this ;
Vue . nextTick ( ( ) => {
vm . $refs . searchField . focus ( ) ;
} ) ;
} ,
getSelected ( ) {
for ( let i = 0 , j = this . items . length ; i < j ; ++ i ) {
if ( this . items [ i ] . selected ) return this . items [ i ] ;
}
return null ;
} ,
getSelectedIndexInFiltered ( ) {
for ( let i = 0 , j = this . filteredItems . length ; i < j ; ++ i ) {
if ( this . filteredItems [ i ] . selected ) return i ;
}
return - 1 ;
} ,
scrollToActive ( ) {
let el = this . $refs [ ` listItem ${ this . active } ` ] ;
if ( ! el || el . length === 0 ) {
return ;
}
if ( Array . isArray ( el ) ) {
el = el [ 0 ] ;
}
const cont = this . $refs . scrollContainer ;
if ( el . offsetTop < cont . scrollTop ) {
cont . scrollTop = el . offsetTop ;
} else if ( el . offsetTop + el . clientHeight > cont . scrollTop + cont . clientHeight ) {
cont . scrollTop = el . offsetTop + el . clientHeight - cont . clientHeight ;
}
} ,
keydown ( event ) {
const vm = this ;
if ( event . keyCode === 40 ) {
// arrow down
event . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( vm . active === - 1 ) {
vm . active = vm . getSelectedIndexInFiltered ( ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
if ( vm . active + ( vm . showCreateNewBranch ? 0 : 1 ) >= vm . filteredItems . length ) {
return ;
}
vm . active ++ ;
vm . scrollToActive ( ) ;
}
if ( event . keyCode === 38 ) {
// arrow up
event . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( vm . active === - 1 ) {
vm . active = vm . getSelectedIndexInFiltered ( ) ;
}
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( vm . active <= 0 ) {
return ;
}
vm . active -- ;
vm . scrollToActive ( ) ;
}
if ( event . keyCode === 13 ) {
// enter
event . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( vm . active >= vm . filteredItems . length ) {
vm . createNewBranch ( ) ;
} else if ( vm . active >= 0 ) {
vm . selectItem ( vm . filteredItems [ vm . active ] ) ;
}
}
if ( event . keyCode === 27 ) {
// escape
event . preventDefault ( ) ;
vm . menuVisible = false ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
}
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
$ ( '.commit-button' ) . click ( function ( e ) {
e . preventDefault ( ) ;
$ ( this ) . parent ( ) . find ( '.commit-body' ) . toggle ( ) ;
} ) ;
function initNavbarContentToggle ( ) {
const content = $ ( '#navbar' ) ;
const toggle = $ ( '#navbar-expand-toggle' ) ;
let isExpanded = false ;
toggle . click ( ( ) => {
isExpanded = ! isExpanded ;
if ( isExpanded ) {
content . addClass ( 'shown' ) ;
toggle . addClass ( 'active' ) ;
} else {
content . removeClass ( 'shown' ) ;
toggle . removeClass ( 'active' ) ;
}
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
function initTopicbar ( ) {
const mgrBtn = $ ( '#manage_topic' ) ;
const editDiv = $ ( '#topic_edit' ) ;
const viewDiv = $ ( '#repo-topics' ) ;
const saveBtn = $ ( '#save_topic' ) ;
const topicDropdown = $ ( '#topic_edit .dropdown' ) ;
const topicForm = $ ( '#topic_edit.ui.form' ) ;
const topicPrompts = getPrompts ( ) ;
mgrBtn . click ( ( ) => {
viewDiv . hide ( ) ;
editDiv . css ( 'display' , '' ) ; // show Semantic UI Grid
} ) ;
function getPrompts ( ) {
const hidePrompt = $ ( 'div.hide#validate_prompt' ) ;
const prompts = {
countPrompt : hidePrompt . children ( '#count_prompt' ) . text ( ) ,
formatPrompt : hidePrompt . children ( '#format_prompt' ) . text ( )
} ;
hidePrompt . remove ( ) ;
return prompts ;
}
saveBtn . click ( ( ) => {
const topics = $ ( 'input[name=topics]' ) . val ( ) ;
$ . post ( saveBtn . data ( 'link' ) , {
_csrf : csrf ,
topics
} , ( _data , _textStatus , xhr ) => {
if ( xhr . responseJSON . status === 'ok' ) {
viewDiv . children ( '.topic' ) . remove ( ) ;
if ( topics . length ) {
const topicArray = topics . split ( ',' ) ;
const last = viewDiv . children ( 'a' ) . last ( ) ;
for ( let i = 0 ; i < topicArray . length ; i ++ ) {
2019-12-18 00:34:11 +03:00
const link = $ ( '<a class="ui repo-topic small label topic"></a>' ) ;
2020-02-12 04:53:18 +03:00
link . attr ( 'href' , ` ${ AppSubUrl } /explore/repos?q= ${ encodeURIComponent ( topicArray [ i ] ) } &topic=1 ` ) ;
2019-12-18 00:34:11 +03:00
link . text ( topicArray [ i ] ) ;
link . insertBefore ( last ) ;
2019-11-15 00:39:51 +03:00
}
}
editDiv . css ( 'display' , 'none' ) ;
viewDiv . show ( ) ;
}
} ) . fail ( ( xhr ) => {
if ( xhr . status === 422 ) {
if ( xhr . responseJSON . invalidTopics . length > 0 ) {
topicPrompts . formatPrompt = xhr . responseJSON . message ;
const { invalidTopics } = xhr . responseJSON ;
const topicLables = topicDropdown . children ( 'a.ui.label' ) ;
topics . split ( ',' ) . forEach ( ( value , index ) => {
for ( let i = 0 ; i < invalidTopics . length ; i ++ ) {
if ( invalidTopics [ i ] === value ) {
topicLables . eq ( index ) . removeClass ( 'green' ) . addClass ( 'red' ) ;
}
}
} ) ;
} else {
topicPrompts . countPrompt = xhr . responseJSON . message ;
}
}
} ) . always ( ( ) => {
topicForm . form ( 'validate form' ) ;
} ) ;
} ) ;
topicDropdown . dropdown ( {
allowAdditions : true ,
forceSelection : false ,
fields : { name : 'description' , value : 'data-value' } ,
saveRemoteData : false ,
label : {
transition : 'horizontal flip' ,
duration : 200 ,
variation : false ,
blue : true ,
basic : true ,
} ,
className : {
label : 'ui small label'
} ,
apiSettings : {
2020-02-12 04:53:18 +03:00
url : ` ${ AppSubUrl } /api/v1/topics/search?q={query} ` ,
2019-11-15 00:39:51 +03:00
throttle : 500 ,
cache : false ,
onResponse ( res ) {
const formattedResponse = {
success : false ,
results : [ ] ,
} ;
const stripTags = function ( text ) {
return text . replace ( /<[^>]*>?/gm , '' ) ;
} ;
const query = stripTags ( this . urlData . query . trim ( ) ) ;
let found _query = false ;
const current _topics = [ ] ;
topicDropdown . find ( 'div.label.visible.topic,a.label.visible' ) . each ( ( _ , e ) => { current _topics . push ( e . dataset . value ) ; } ) ;
if ( res . topics ) {
let found = false ;
for ( let i = 0 ; i < res . topics . length ; i ++ ) {
// skip currently added tags
if ( current _topics . indexOf ( res . topics [ i ] . topic _name ) !== - 1 ) {
continue ;
}
if ( res . topics [ i ] . topic _name . toLowerCase ( ) === query . toLowerCase ( ) ) {
found _query = true ;
}
formattedResponse . results . push ( { description : res . topics [ i ] . topic _name , 'data-value' : res . topics [ i ] . topic _name } ) ;
found = true ;
}
formattedResponse . success = found ;
}
if ( query . length > 0 && ! found _query ) {
formattedResponse . success = true ;
formattedResponse . results . unshift ( { description : query , 'data-value' : query } ) ;
} else if ( query . length > 0 && found _query ) {
formattedResponse . results . sort ( ( a , b ) => {
if ( a . description . toLowerCase ( ) === query . toLowerCase ( ) ) return - 1 ;
if ( b . description . toLowerCase ( ) === query . toLowerCase ( ) ) return 1 ;
if ( a . description > b . description ) return - 1 ;
if ( a . description < b . description ) return 1 ;
return 0 ;
} ) ;
}
return formattedResponse ;
} ,
} ,
onLabelCreate ( value ) {
value = value . toLowerCase ( ) . trim ( ) ;
this . attr ( 'data-value' , value ) . contents ( ) . first ( ) . replaceWith ( value ) ;
return $ ( this ) ;
} ,
onAdd ( addedValue , _addedText , $addedChoice ) {
addedValue = addedValue . toLowerCase ( ) . trim ( ) ;
$ ( $addedChoice ) . attr ( 'data-value' , addedValue ) ;
$ ( $addedChoice ) . attr ( 'data-text' , addedValue ) ;
}
} ) ;
$ . fn . form . settings . rules . validateTopic = function ( _values , regExp ) {
const topics = topicDropdown . children ( 'a.ui.label' ) ;
const status = topics . length === 0 || topics . last ( ) . attr ( 'data-value' ) . match ( regExp ) ;
if ( ! status ) {
topics . last ( ) . removeClass ( 'green' ) . addClass ( 'red' ) ;
}
return status && topicDropdown . children ( 'a.ui.label.red' ) . length === 0 ;
} ;
topicForm . form ( {
on : 'change' ,
inline : true ,
fields : {
topics : {
identifier : 'topics' ,
rules : [
{
type : 'validateTopic' ,
2019-12-02 12:13:19 +03:00
value : /^[a-z0-9][a-z0-9-]{0,35}$/ ,
2019-11-15 00:39:51 +03:00
prompt : topicPrompts . formatPrompt
} ,
{
type : 'maxCount[25]' ,
prompt : topicPrompts . countPrompt
}
]
} ,
}
} ) ;
}
window . toggleDeadlineForm = function ( ) {
$ ( '#deadlineForm' ) . fadeToggle ( 150 ) ;
} ;
window . setDeadline = function ( ) {
const deadline = $ ( '#deadlineDate' ) . val ( ) ;
window . updateDeadline ( deadline ) ;
} ;
window . updateDeadline = function ( deadlineString ) {
$ ( '#deadline-err-invalid-date' ) . hide ( ) ;
$ ( '#deadline-loader' ) . addClass ( 'loading' ) ;
let realDeadline = null ;
if ( deadlineString !== '' ) {
const newDate = Date . parse ( deadlineString ) ;
if ( Number . isNaN ( newDate ) ) {
$ ( '#deadline-loader' ) . removeClass ( 'loading' ) ;
$ ( '#deadline-err-invalid-date' ) . show ( ) ;
return false ;
}
realDeadline = new Date ( newDate ) ;
}
$ . ajax ( ` ${ $ ( '#update-issue-deadline-form' ) . attr ( 'action' ) } /deadline ` , {
data : JSON . stringify ( {
due _date : realDeadline ,
} ) ,
headers : {
'X-Csrf-Token' : csrf ,
'X-Remote' : true ,
} ,
contentType : 'application/json' ,
type : 'POST' ,
success ( ) {
reload ( ) ;
} ,
error ( ) {
$ ( '#deadline-loader' ) . removeClass ( 'loading' ) ;
$ ( '#deadline-err-invalid-date' ) . show ( ) ;
}
} ) ;
} ;
window . deleteDependencyModal = function ( id , type ) {
$ ( '.remove-dependency' )
. modal ( {
closable : false ,
duration : 200 ,
onApprove ( ) {
$ ( '#removeDependencyID' ) . val ( id ) ;
$ ( '#dependencyType' ) . val ( type ) ;
$ ( '#removeDependencyForm' ) . submit ( ) ;
}
} ) . modal ( 'show' ) ;
} ;
2019-11-13 17:52:13 +03:00
function initIssueList ( ) {
2019-11-15 00:39:51 +03:00
const repolink = $ ( '#repolink' ) . val ( ) ;
const repoId = $ ( '#repoId' ) . val ( ) ;
const crossRepoSearch = $ ( '#crossRepoSearch' ) . val ( ) ;
2020-01-19 09:43:38 +03:00
const tp = $ ( '#type' ) . val ( ) ;
2020-02-12 04:53:18 +03:00
let issueSearchUrl = ` ${ AppSubUrl } /api/v1/repos/ ${ repolink } /issues?q={query}&type= ${ tp } ` ;
2019-11-15 00:39:51 +03:00
if ( crossRepoSearch === 'true' ) {
2020-02-12 04:53:18 +03:00
issueSearchUrl = ` ${ AppSubUrl } /api/v1/repos/issues/search?q={query}&priority_repo_id= ${ repoId } &type= ${ tp } ` ;
2019-11-15 00:39:51 +03:00
}
$ ( '#new-dependency-drop-list' )
. dropdown ( {
apiSettings : {
url : issueSearchUrl ,
onResponse ( response ) {
const filteredResponse = { success : true , results : [ ] } ;
const currIssueId = $ ( '#new-dependency-drop-list' ) . data ( 'issue-id' ) ;
// Parse the response from the api to work with our dropdown
$ . each ( response , ( _i , issue ) => {
// Don't list current issue in the dependency list.
if ( issue . id === currIssueId ) {
return ;
}
filteredResponse . results . push ( {
name : ` # ${ issue . number } ${ htmlEncode ( issue . title )
} < div class = "text small dont-break-out" > $ { htmlEncode ( issue . repository . full _name ) } < / d i v > ` ,
value : issue . id
} ) ;
} ) ;
return filteredResponse ;
} ,
cache : false ,
} ,
fullTextSearch : true
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$ ( '.menu a.label-filter-item' ) . each ( function ( ) {
$ ( this ) . click ( function ( e ) {
if ( e . altKey ) {
e . preventDefault ( ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const href = $ ( this ) . attr ( 'href' ) ;
const id = $ ( this ) . data ( 'label-id' ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const regStr = ` labels=(-?[0-9]+%2c)*( ${ id } )(%2c-?[0-9]+)*& ` ;
const newStr = 'labels=$1-$2$3&' ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
window . location = href . replace ( new RegExp ( regStr ) , newStr ) ;
}
2019-11-13 17:52:13 +03:00
} ) ;
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
$ ( '.menu .ui.dropdown.label-filter' ) . keydown ( ( e ) => {
if ( e . altKey && e . keyCode === 13 ) {
const selectedItems = $ ( '.menu .ui.dropdown.label-filter .menu .item.selected' ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
if ( selectedItems . length > 0 ) {
const item = $ ( selectedItems [ 0 ] ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const href = item . attr ( 'href' ) ;
const id = item . data ( 'label-id' ) ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
const regStr = ` labels=(-?[0-9]+%2c)*( ${ id } )(%2c-?[0-9]+)*& ` ;
const newStr = 'labels=$1-$2$3&' ;
2019-11-13 17:52:13 +03:00
2019-11-15 00:39:51 +03:00
window . location = href . replace ( new RegExp ( regStr ) , newStr ) ;
}
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
} ) ;
2019-11-13 17:52:13 +03:00
}
2019-11-15 00:39:51 +03:00
window . cancelCodeComment = function ( btn ) {
const form = $ ( btn ) . closest ( 'form' ) ;
if ( form . length > 0 && form . hasClass ( 'comment-form' ) ) {
form . addClass ( 'hide' ) ;
form . parent ( ) . find ( 'button.comment-form-reply' ) . show ( ) ;
} else {
form . closest ( '.comment-code-cloud' ) . remove ( ) ;
}
} ;
2020-02-13 08:22:15 +03:00
window . submitReply = function ( btn ) {
const form = $ ( btn ) . closest ( 'form' ) ;
if ( form . length > 0 && form . hasClass ( 'comment-form' ) ) {
form . submit ( ) ;
}
} ;
2019-11-15 00:39:51 +03:00
window . onOAuthLoginClick = function ( ) {
const oauthLoader = $ ( '#oauth2-login-loader' ) ;
const oauthNav = $ ( '#oauth2-login-navigator' ) ;
oauthNav . hide ( ) ;
oauthLoader . removeClass ( 'disabled' ) ;
setTimeout ( ( ) => {
// recover previous content to let user try again
// usually redirection will be performed before this action
oauthLoader . addClass ( 'disabled' ) ;
oauthNav . show ( ) ;
} , 5000 ) ;
} ;
2020-02-18 02:11:59 +03:00
// Pull SVGs via AJAX to workaround CORS issues with <use> tags
// https://css-tricks.com/ajaxing-svg-sprite/
$ . get ( ` ${ window . config . StaticUrlPrefix } /img/svg/icons.svg ` , ( data ) => {
const div = document . createElement ( 'div' ) ;
div . style . display = 'none' ;
div . innerHTML = new XMLSerializer ( ) . serializeToString ( data . documentElement ) ;
document . body . insertBefore ( div , document . body . childNodes [ 0 ] ) ;
} ) ;