1
- import $ from 'jquery' ;
2
1
import { svg } from '../svg.ts' ;
3
- import { invertFileFolding } from './file-fold.ts' ;
4
2
import { createTippy } from '../modules/tippy.ts' ;
5
3
import { clippie } from 'clippie' ;
6
4
import { toAbsoluteUrl } from '../utils.ts' ;
7
-
8
- export const singleAnchorRegex = / ^ # ( L | n ) ( [ 1 - 9 ] [ 0 - 9 ] * ) $ / ;
9
- export const rangeAnchorRegex = / ^ # ( L [ 1 - 9 ] [ 0 - 9 ] * ) - ( L [ 1 - 9 ] [ 0 - 9 ] * ) $ / ;
5
+ import { addDelegatedEventListener } from '../utils/dom.ts' ;
10
6
11
7
function changeHash ( hash : string ) {
12
8
if ( window . history . pushState ) {
@@ -16,20 +12,11 @@ function changeHash(hash: string) {
16
12
}
17
13
}
18
14
19
- function isBlame ( ) {
20
- return Boolean ( document . querySelector ( 'div.blame' ) ) ;
21
- }
15
+ // it selects the code lines defined by range: `L1-L3` (3 lines) or `L2` (singe line)
16
+ function selectRange ( range : string ) : Element {
17
+ for ( const el of document . querySelectorAll ( '.code-view tr.active' ) ) el . classList . remove ( 'active' ) ;
18
+ const elLineNums = document . querySelectorAll ( `.code-view td.lines-num span[data-line-number]` ) ;
22
19
23
- function getLineEls ( ) {
24
- return document . querySelectorAll ( `.code-view td.lines-code${ isBlame ( ) ? '.blame-code' : '' } ` ) ;
25
- }
26
-
27
- function selectRange ( $linesEls , $selectionEndEl , $selectionStartEls ?) {
28
- for ( const el of $linesEls ) {
29
- el . closest ( 'tr' ) . classList . remove ( 'active' ) ;
30
- }
31
-
32
- // add hashchange to permalink
33
20
const refInNewIssue = document . querySelector ( 'a.ref-in-new-issue' ) ;
34
21
const copyPermalink = document . querySelector ( 'a.copy-line-permalink' ) ;
35
22
const viewGitBlame = document . querySelector ( 'a.view_git_blame' ) ;
@@ -59,37 +46,30 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
59
46
copyPermalink . setAttribute ( 'data-url' , link ) ;
60
47
} ;
61
48
62
- if ( $selectionStartEls ) {
63
- let a = parseInt ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) . slice ( 1 ) ) ;
64
- let b = parseInt ( $selectionStartEls [ 0 ] . getAttribute ( 'rel' ) . slice ( 1 ) ) ;
65
- let c ;
66
- if ( a !== b ) {
67
- if ( a > b ) {
68
- c = a ;
69
- a = b ;
70
- b = c ;
71
- }
72
- const classes = [ ] ;
73
- for ( let i = a ; i <= b ; i ++ ) {
74
- classes . push ( `[rel=L${ i } ]` ) ;
75
- }
76
- $linesEls . filter ( classes . join ( ',' ) ) . each ( function ( ) {
77
- this . closest ( 'tr' ) . classList . add ( 'active' ) ;
78
- } ) ;
79
- changeHash ( `#L${ a } -L${ b } ` ) ;
80
-
81
- updateIssueHref ( `L${ a } -L${ b } ` ) ;
82
- updateViewGitBlameFragment ( `L${ a } -L${ b } ` ) ;
83
- updateCopyPermalinkUrl ( `L${ a } -L${ b } ` ) ;
84
- return ;
85
- }
49
+ const rangeFields = range ? range . split ( '-' ) : [ ] ;
50
+ const start = rangeFields [ 0 ] ?? '' ;
51
+ if ( ! start ) return null ;
52
+ const stop = rangeFields [ 1 ] || start ;
53
+
54
+ // format is i.e. 'L14-L26'
55
+ let startLineNum = parseInt ( start . substring ( 1 ) ) ;
56
+ let stopLineNum = parseInt ( stop . substring ( 1 ) ) ;
57
+ if ( startLineNum > stopLineNum ) {
58
+ const tmp = startLineNum ;
59
+ startLineNum = stopLineNum ;
60
+ stopLineNum = tmp ;
61
+ range = `${ stop } -${ start } ` ;
86
62
}
87
- $selectionEndEl [ 0 ] . closest ( 'tr' ) . classList . add ( 'active' ) ;
88
- changeHash ( `#${ $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) } ` ) ;
89
63
90
- updateIssueHref ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
91
- updateViewGitBlameFragment ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
92
- updateCopyPermalinkUrl ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
64
+ const first = elLineNums [ startLineNum - 1 ] ?? null ;
65
+ for ( let i = startLineNum - 1 ; i <= stopLineNum - 1 && i < elLineNums . length ; i ++ ) {
66
+ elLineNums [ i ] . closest ( 'tr' ) . classList . add ( 'active' ) ;
67
+ }
68
+ changeHash ( `#${ range } ` ) ;
69
+ updateIssueHref ( range ) ;
70
+ updateViewGitBlameFragment ( range ) ;
71
+ updateCopyPermalinkUrl ( range ) ;
72
+ return first ;
93
73
}
94
74
95
75
function showLineButton ( ) {
@@ -103,6 +83,8 @@ function showLineButton() {
103
83
104
84
// find active row and add button
105
85
const tr = document . querySelector ( '.code-view tr.active' ) ;
86
+ if ( ! tr ) return ;
87
+
106
88
const td = tr . querySelector ( 'td.lines-num' ) ;
107
89
const btn = document . createElement ( 'button' ) ;
108
90
btn . classList . add ( 'code-line-button' , 'ui' , 'basic' , 'button' ) ;
@@ -128,62 +110,36 @@ function showLineButton() {
128
110
}
129
111
130
112
export function initRepoCodeView ( ) {
131
- if ( $ ( '.code-view .lines-num' ) . length > 0 ) {
132
- $ ( document ) . on ( 'click' , '.lines-num span' , function ( e ) {
133
- const linesEls = getLineEls ( ) ;
134
- const selectedEls = Array . from ( linesEls ) . filter ( ( el ) => {
135
- return el . matches ( `[rel=${ this . getAttribute ( 'id' ) } ]` ) ;
136
- } ) ;
137
-
138
- let from ;
139
- if ( e . shiftKey ) {
140
- from = Array . from ( linesEls ) . filter ( ( el ) => {
141
- return el . closest ( 'tr' ) . classList . contains ( 'active' ) ;
142
- } ) ;
143
- }
144
- selectRange ( $ ( linesEls ) , $ ( selectedEls ) , from ? $ ( from ) : null ) ;
145
- window . getSelection ( ) . removeAllRanges ( ) ;
146
- showLineButton ( ) ;
147
- } ) ;
148
-
149
- $ ( window ) . on ( 'hashchange' , ( ) => {
150
- let m = rangeAnchorRegex . exec ( window . location . hash ) ;
151
- const $linesEls = $ ( getLineEls ( ) ) ;
152
- let $first ;
153
- if ( m ) {
154
- $first = $linesEls . filter ( `[rel=${ m [ 1 ] } ]` ) ;
155
- if ( $first . length ) {
156
- selectRange ( $linesEls , $first , $linesEls . filter ( `[rel=${ m [ 2 ] } ]` ) ) ;
157
-
158
- // show code view menu marker (don't show in blame page)
159
- if ( ! isBlame ( ) ) {
160
- showLineButton ( ) ;
161
- }
162
-
163
- $ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
164
- return ;
165
- }
166
- }
167
- m = singleAnchorRegex . exec ( window . location . hash ) ;
168
- if ( m ) {
169
- $first = $linesEls . filter ( `[rel=L${ m [ 2 ] } ]` ) ;
170
- if ( $first . length ) {
171
- selectRange ( $linesEls , $first ) ;
172
-
173
- // show code view menu marker (don't show in blame page)
174
- if ( ! isBlame ( ) ) {
175
- showLineButton ( ) ;
176
- }
177
-
178
- $ ( 'html, body' ) . scrollTop ( $first . offset ( ) . top - 200 ) ;
179
- }
180
- }
181
- } ) . trigger ( 'hashchange' ) ;
182
- }
183
- $ ( document ) . on ( 'click' , '.fold-file' , ( { currentTarget} ) => {
184
- invertFileFolding ( currentTarget . closest ( '.file-content' ) , currentTarget ) ;
113
+ if ( ! document . querySelector ( '.code-view .lines-num' ) ) return ;
114
+
115
+ let selRangeStart : string ;
116
+ addDelegatedEventListener ( document , 'click' , '.lines-num span' , ( el : HTMLElement , e : KeyboardEvent ) => {
117
+ if ( ! selRangeStart || ! e . shiftKey ) {
118
+ selRangeStart = el . getAttribute ( 'id' ) ;
119
+ selectRange ( selRangeStart ) ;
120
+ } else {
121
+ const selRangeStop = el . getAttribute ( 'id' ) ;
122
+ selectRange ( `${ selRangeStart } -${ selRangeStop } ` ) ;
123
+ }
124
+ window . getSelection ( ) . removeAllRanges ( ) ;
125
+ showLineButton ( ) ;
185
126
} ) ;
186
- $ ( document ) . on ( 'click' , '.copy-line-permalink' , async ( { currentTarget} ) => {
187
- await clippie ( toAbsoluteUrl ( currentTarget . getAttribute ( 'data-url' ) ) ) ;
127
+
128
+ const onHashChange = ( ) => {
129
+ if ( ! window . location . hash ) return ;
130
+ const range = window . location . hash . substring ( 1 ) ;
131
+ const first = selectRange ( range ) ;
132
+ if ( first ) {
133
+ // set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
134
+ if ( window . history . scrollRestoration !== 'manual' ) window . history . scrollRestoration = 'manual' ;
135
+ first . scrollIntoView ( { block : 'start' } ) ;
136
+ showLineButton ( ) ;
137
+ }
138
+ } ;
139
+ onHashChange ( ) ;
140
+ window . addEventListener ( 'hashchange' , onHashChange ) ;
141
+
142
+ addDelegatedEventListener ( document , 'click' , '.copy-line-permalink' , ( el ) => {
143
+ clippie ( toAbsoluteUrl ( el . getAttribute ( 'data-url' ) ) ) ;
188
144
} ) ;
189
145
}
0 commit comments