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,10 @@ function changeHash(hash: string) {
16
12
}
17
13
}
18
14
19
- function isBlame ( ) {
20
- return Boolean ( document . querySelector ( 'div.blame' ) ) ;
21
- }
15
+ function selectRange ( range : string ) : Element {
16
+ for ( const el of document . querySelectorAll ( '.code-view tr.active' ) ) el . classList . remove ( 'active' ) ;
17
+ const elLineNums = document . querySelectorAll ( `.code-view td.lines-num span[data-line-number]` ) ;
22
18
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
19
const refInNewIssue = document . querySelector ( 'a.ref-in-new-issue' ) ;
34
20
const copyPermalink = document . querySelector ( 'a.copy-line-permalink' ) ;
35
21
const viewGitBlame = document . querySelector ( 'a.view_git_blame' ) ;
@@ -59,37 +45,29 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
59
45
copyPermalink . setAttribute ( 'data-url' , link ) ;
60
46
} ;
61
47
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
- }
48
+ const rangeFields = range ? range . split ( '-' ) : [ ] ;
49
+ const start = rangeFields [ 0 ] ?? '' ;
50
+ if ( ! start ) return null ;
51
+ const stop = rangeFields [ 1 ] || start ;
52
+
53
+ let startLineNum = parseInt ( start . substring ( 1 ) ) ;
54
+ let stopLineNum = parseInt ( stop . substring ( 1 ) ) ;
55
+ if ( startLineNum > stopLineNum ) {
56
+ const tmp = startLineNum ;
57
+ startLineNum = stopLineNum ;
58
+ stopLineNum = tmp ;
59
+ range = `${ stop } -${ start } ` ;
86
60
}
87
- $selectionEndEl [ 0 ] . closest ( 'tr' ) . classList . add ( 'active' ) ;
88
- changeHash ( `#${ $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) } ` ) ;
89
61
90
- updateIssueHref ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
91
- updateViewGitBlameFragment ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
92
- updateCopyPermalinkUrl ( $selectionEndEl [ 0 ] . getAttribute ( 'rel' ) ) ;
62
+ const first = elLineNums [ startLineNum ] ?? null ;
63
+ for ( let i = startLineNum - 1 ; i <= stopLineNum - 1 && i < elLineNums . length ; i ++ ) {
64
+ elLineNums [ i ] . closest ( 'tr' ) . classList . add ( 'active' ) ;
65
+ }
66
+ changeHash ( `#${ range } ` ) ;
67
+ updateIssueHref ( range ) ;
68
+ updateViewGitBlameFragment ( range ) ;
69
+ updateCopyPermalinkUrl ( range ) ;
70
+ return first ;
93
71
}
94
72
95
73
function showLineButton ( ) {
@@ -103,6 +81,8 @@ function showLineButton() {
103
81
104
82
// find active row and add button
105
83
const tr = document . querySelector ( '.code-view tr.active' ) ;
84
+ if ( ! tr ) return ;
85
+
106
86
const td = tr . querySelector ( 'td.lines-num' ) ;
107
87
const btn = document . createElement ( 'button' ) ;
108
88
btn . classList . add ( 'code-line-button' , 'ui' , 'basic' , 'button' ) ;
@@ -128,62 +108,36 @@ function showLineButton() {
128
108
}
129
109
130
110
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 ) ;
111
+ if ( ! document . querySelector ( '.code-view .lines-num' ) ) return ;
112
+
113
+ let selRangeStart : string ;
114
+ addDelegatedEventListener ( document , 'click' , '.lines-num span' , ( el : HTMLElement , e : KeyboardEvent ) => {
115
+ if ( ! selRangeStart || ! e . shiftKey ) {
116
+ selRangeStart = el . getAttribute ( 'id' ) ;
117
+ selectRange ( selRangeStart ) ;
118
+ } else {
119
+ const selRangeStop = el . getAttribute ( 'id' ) ;
120
+ selectRange ( `${ selRangeStart } -${ selRangeStop } ` ) ;
121
+ }
122
+ window . getSelection ( ) . removeAllRanges ( ) ;
123
+ showLineButton ( ) ;
185
124
} ) ;
186
- $ ( document ) . on ( 'click' , '.copy-line-permalink' , async ( { currentTarget} ) => {
187
- await clippie ( toAbsoluteUrl ( currentTarget . getAttribute ( 'data-url' ) ) ) ;
125
+
126
+ const onHashChange = ( ) => {
127
+ if ( ! window . location . hash ) return ;
128
+ const range = window . location . hash . substring ( 1 ) ;
129
+ const first = selectRange ( range ) ;
130
+ if ( first ) {
131
+ // set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
132
+ if ( window . history . scrollRestoration !== 'manual' ) window . history . scrollRestoration = 'manual' ;
133
+ first . scrollIntoView ( { block : 'start' } ) ;
134
+ showLineButton ( ) ;
135
+ }
136
+ } ;
137
+ onHashChange ( ) ;
138
+ window . addEventListener ( 'hashchange' , onHashChange ) ;
139
+
140
+ addDelegatedEventListener ( document , 'click' , '.copy-line-permalink' , ( _ , e ) => {
141
+ clippie ( toAbsoluteUrl ( ( e . currentTarget as HTMLElement ) . getAttribute ( 'data-url' ) ) ) ;
188
142
} ) ;
189
143
}
0 commit comments