2022-01-29 00:00:11 +03:00
import $ from 'jquery' ;
2021-10-16 20:28:04 +03:00
import { svg } from '../svg.js' ;
2022-05-07 21:28:10 +03:00
import { invertFileFolding } from './file-fold.js' ;
2022-11-21 12:59:42 +03:00
import { createTippy } from '../modules/tippy.js' ;
2023-04-02 12:25:36 +03:00
import { clippie } from 'clippie' ;
2023-02-07 19:08:44 +03:00
import { toAbsoluteUrl } from '../utils.js' ;
2021-10-16 20:28:04 +03:00
2022-11-11 13:22:36 +03:00
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/ ;
export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/ ;
2022-11-04 22:33:50 +03:00
2021-10-16 20:28:04 +03:00
function changeHash ( hash ) {
if ( window . history . pushState ) {
window . history . pushState ( null , null , hash ) ;
} else {
window . location . hash = hash ;
}
}
function selectRange ( $list , $select , $from ) {
$list . removeClass ( 'active' ) ;
// add hashchange to permalink
2023-02-07 19:08:44 +03:00
const $refInNewIssue = $ ( 'a.ref-in-new-issue' ) ;
2021-10-16 20:28:04 +03:00
const $copyPermalink = $ ( 'a.copy-line-permalink' ) ;
2022-04-26 13:54:40 +03:00
const $viewGitBlame = $ ( 'a.view_git_blame' ) ;
2021-10-16 20:28:04 +03:00
2022-02-12 08:00:24 +03:00
const updateIssueHref = function ( anchor ) {
2023-02-07 19:08:44 +03:00
if ( $refInNewIssue . length === 0 ) {
2022-02-12 08:00:24 +03:00
return ;
}
2023-02-07 19:08:44 +03:00
const urlIssueNew = $refInNewIssue . attr ( 'data-url-issue-new' ) ;
const urlParamBodyLink = $refInNewIssue . attr ( 'data-url-param-body-link' ) ;
const issueContent = ` ${ toAbsoluteUrl ( urlParamBodyLink ) } # ${ anchor } ` ; // the default content for issue body
$refInNewIssue . attr ( 'href' , ` ${ urlIssueNew } ?body= ${ encodeURIComponent ( issueContent ) } ` ) ;
2021-10-16 20:28:04 +03:00
} ;
2022-04-26 13:54:40 +03:00
const updateViewGitBlameFragment = function ( anchor ) {
if ( $viewGitBlame . length === 0 ) {
return ;
}
let href = $viewGitBlame . attr ( 'href' ) ;
href = ` ${ href . replace ( /#L\d+$|#L\d+-L\d+$/ , '' ) } ` ;
if ( anchor . length !== 0 ) {
href = ` ${ href } # ${ anchor } ` ;
}
$viewGitBlame . attr ( 'href' , href ) ;
} ;
2022-08-09 15:37:34 +03:00
const updateCopyPermalinkUrl = function ( anchor ) {
2022-04-26 13:54:40 +03:00
if ( $copyPermalink . length === 0 ) {
return ;
}
2022-08-09 15:37:34 +03:00
let link = $copyPermalink . attr ( 'data-url' ) ;
2021-10-16 20:28:04 +03:00
link = ` ${ link . replace ( /#L\d+$|#L\d+-L\d+$/ , '' ) } # ${ anchor } ` ;
2022-08-09 15:37:34 +03:00
$copyPermalink . attr ( 'data-url' , link ) ;
2021-10-16 20:28:04 +03:00
} ;
if ( $from ) {
2022-02-18 09:50:36 +03:00
let a = parseInt ( $select . attr ( 'rel' ) . slice ( 1 ) ) ;
let b = parseInt ( $from . attr ( 'rel' ) . slice ( 1 ) ) ;
2021-10-16 20:28:04 +03:00
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 ( ` [rel=L ${ i } ] ` ) ;
}
$list . filter ( classes . join ( ',' ) ) . addClass ( 'active' ) ;
changeHash ( ` #L ${ a } -L ${ b } ` ) ;
updateIssueHref ( ` L ${ a } -L ${ b } ` ) ;
2022-04-26 13:54:40 +03:00
updateViewGitBlameFragment ( ` L ${ a } -L ${ b } ` ) ;
2022-08-09 15:37:34 +03:00
updateCopyPermalinkUrl ( ` L ${ a } -L ${ b } ` ) ;
2021-10-16 20:28:04 +03:00
return ;
}
}
$select . addClass ( 'active' ) ;
changeHash ( ` # ${ $select . attr ( 'rel' ) } ` ) ;
updateIssueHref ( $select . attr ( 'rel' ) ) ;
2022-04-26 13:54:40 +03:00
updateViewGitBlameFragment ( $select . attr ( 'rel' ) ) ;
2022-08-09 15:37:34 +03:00
updateCopyPermalinkUrl ( $select . attr ( 'rel' ) ) ;
2021-10-16 20:28:04 +03:00
}
function showLineButton ( ) {
2022-08-09 15:37:34 +03:00
const menu = document . querySelector ( '.code-line-menu' ) ;
if ( ! menu ) return ;
// remove all other line buttons
for ( const el of document . querySelectorAll ( '.code-line-button' ) ) {
el . remove ( ) ;
}
// find active row and add button
const tr = document . querySelector ( '.code-view td.lines-code.active' ) . closest ( 'tr' ) ;
const td = tr . querySelector ( 'td' ) ;
const btn = document . createElement ( 'button' ) ;
btn . classList . add ( 'code-line-button' ) ;
btn . innerHTML = svg ( 'octicon-kebab-horizontal' ) ;
td . prepend ( btn ) ;
// put a copy of the menu back into DOM for the next click
2023-05-09 05:35:49 +03:00
btn . closest ( '.code-view' ) . append ( menu . cloneNode ( true ) ) ;
2022-08-09 15:37:34 +03:00
createTippy ( btn , {
trigger : 'click' ,
content : menu ,
placement : 'right-start' ,
role : 'menu' ,
interactive : 'true' ,
} ) ;
2021-10-16 20:28:04 +03:00
}
export function initRepoCodeView ( ) {
if ( $ ( '.code-view .lines-num' ) . length > 0 ) {
$ ( document ) . on ( 'click' , '.lines-num span' , function ( e ) {
const $select = $ ( this ) ;
let $list ;
if ( $ ( 'div.blame' ) . length ) {
$list = $ ( '.code-view td.lines-code.blame-code' ) ;
} else {
$list = $ ( '.code-view td.lines-code' ) ;
}
selectRange ( $list , $list . filter ( ` [rel= ${ $select . attr ( 'id' ) } ] ` ) , ( e . shiftKey ? $list . filter ( '.active' ) . eq ( 0 ) : null ) ) ;
if ( window . getSelection ) {
window . getSelection ( ) . removeAllRanges ( ) ;
} else {
document . selection . empty ( ) ;
}
// show code view menu marker (don't show in blame page)
if ( $ ( 'div.blame' ) . length === 0 ) {
showLineButton ( ) ;
}
} ) ;
$ ( window ) . on ( 'hashchange' , ( ) => {
2022-11-11 13:22:36 +03:00
let m = window . location . hash . match ( rangeAnchorRegex ) ;
2021-10-16 20:28:04 +03:00
let $list ;
if ( $ ( 'div.blame' ) . length ) {
$list = $ ( '.code-view td.lines-code.blame-code' ) ;
} else {
$list = $ ( '.code-view td.lines-code' ) ;
}
let $first ;
if ( m ) {
$first = $list . filter ( ` [rel= ${ m [ 1 ] } ] ` ) ;
2022-11-11 13:22:36 +03:00
if ( $first . length ) {
selectRange ( $list , $first , $list . filter ( ` [rel= ${ m [ 2 ] } ] ` ) ) ;
2021-10-16 20:28:04 +03:00
2022-11-11 13:22:36 +03:00
// show code view menu marker (don't show in blame page)
if ( $ ( 'div.blame' ) . length === 0 ) {
showLineButton ( ) ;
}
2021-10-16 20:28:04 +03:00
2022-11-11 13:22:36 +03:00
$ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
return ;
}
2021-10-16 20:28:04 +03:00
}
2022-11-11 13:22:36 +03:00
m = window . location . hash . match ( singleAnchorRegex ) ;
2021-10-16 20:28:04 +03:00
if ( m ) {
$first = $list . filter ( ` [rel=L ${ m [ 2 ] } ] ` ) ;
2022-11-11 13:22:36 +03:00
if ( $first . length ) {
selectRange ( $list , $first ) ;
2021-10-16 20:28:04 +03:00
2022-11-11 13:22:36 +03:00
// show code view menu marker (don't show in blame page)
if ( $ ( 'div.blame' ) . length === 0 ) {
showLineButton ( ) ;
}
2021-10-16 20:28:04 +03:00
2022-11-11 13:22:36 +03:00
$ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
}
2021-10-16 20:28:04 +03:00
}
} ) . trigger ( 'hashchange' ) ;
}
$ ( document ) . on ( 'click' , '.fold-file' , ( { currentTarget } ) => {
2022-05-07 21:28:10 +03:00
invertFileFolding ( currentTarget . closest ( '.file-content' ) , currentTarget ) ;
2021-10-16 20:28:04 +03:00
} ) ;
2023-05-21 23:47:41 +03:00
$ ( document ) . on ( 'click' , '.code-expander-button' , async ( { currentTarget } ) => {
2021-11-22 11:19:01 +03:00
const url = currentTarget . getAttribute ( 'data-url' ) ;
const query = currentTarget . getAttribute ( 'data-query' ) ;
const anchor = currentTarget . getAttribute ( 'data-anchor' ) ;
2021-10-16 20:28:04 +03:00
if ( ! url ) return ;
const blob = await $ . get ( ` ${ url } ? ${ query } &anchor= ${ anchor } ` ) ;
currentTarget . closest ( 'tr' ) . outerHTML = blob ;
} ) ;
2022-08-09 15:37:34 +03:00
$ ( document ) . on ( 'click' , '.copy-line-permalink' , async ( e ) => {
2023-05-28 04:34:18 +03:00
await clippie ( toAbsoluteUrl ( e . currentTarget . getAttribute ( 'data-url' ) ) ) ;
2022-08-09 15:37:34 +03:00
} ) ;
2021-10-16 20:28:04 +03:00
}