1
+ // ==UserScript==
2
+ // @name Github助手
3
+ // @namespace https://github.com/yeomanye
4
+ // @version 0.7.1
5
+ // @description 添加Github文件下载、复制按钮、图片点击放大(右击恢复)、issues中只查看用户相关态度的内容、issues列表项从新标签页打开
6
+ // @require https://greasyfork.org/scripts/34143-debug/code/debug.js?version=246342
7
+ // @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js
8
+ // @require https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js
9
+ // @require https://cdn.bootcss.com/jquery.pjax/1.1.0/jquery.pjax.min.js
10
+ // @require https://greasyfork.org/scripts/53536-ui/code/UI.js?version=281393
11
+ // @author Ming Ye
12
+ // @match https://github.com
13
+ // @include https://github.com/*/*
14
+ // @grant none
15
+ // ==/UserScript==
16
+
17
+ ( function ( ) {
18
+ 'use strict' ;
19
+ myDebugger . debugD = false ;
20
+ var log = myDebugger . consoleFactory ( "github-btn" , "log" , null ) ;
21
+ var debugTrue = myDebugger . debugTrue ;
22
+ var href = ___location . href ;
23
+ /**
24
+ * 初始化函数
25
+ * @return {[type] } [description]
26
+ */
27
+ function init ( ) {
28
+ createDownLink ( ) ;
29
+ createCopyLink ( ) ;
30
+ bindImgClick ( ) ;
31
+ createFilterPanel ( ) ;
32
+ openIssueFromNew ( ) ;
33
+ }
34
+ /**
35
+ * 创建下载链接
36
+ * @return {[type] } [description]
37
+ */
38
+ function createDownLink ( ) {
39
+ //如果不是repository页面则直接返回
40
+ var $files = $ ( '.octicon.octicon-file' ) ;
41
+ var $directory = $ ( '.octicon.octicon-file-directory' ) ;
42
+ //var $directory = $('.js-navigation-open');
43
+ if ( $files . length === 0 && $directory . length === 0 ) return ;
44
+ var mouseOverHandler = function ( evt ) {
45
+ // debugTrue();
46
+ var elem = evt . currentTarget ,
47
+ aElm = elem . querySelector ( '.fileDownLink' ) ;
48
+ aElm . style . visibility = 'visible' ;
49
+ } ;
50
+
51
+ var mouseOutHandler = function ( evt ) {
52
+ // debugTrue();
53
+ var elem = evt . currentTarget ,
54
+ aElm = elem . querySelector ( '.fileDownLink' ) ;
55
+ aElm . style . visibility = 'hidden' ;
56
+ } ;
57
+
58
+ var linkClick = function ( evt ) {
59
+ var elem = evt . currentTarget ;
60
+ var $link = $ ( '<a></a>' ) ;
61
+ $link . attr ( 'href' , elem . getAttribute ( 'download-url' ) ) ;
62
+ $link . attr ( 'download' , elem . getAttribute ( 'filename' ) ) ;
63
+ $link . get ( 0 ) . click ( ) ;
64
+ } ;
65
+
66
+ // debugTrue();
67
+ var origin = ___location . origin ,
68
+ href = ___location . href ,
69
+ path = href . replace ( origin , '' ) ;
70
+ if ( path . indexOf ( 'tree' ) < 0 )
71
+ path += '/tree/master/' ;
72
+ path = path . replace ( 'tree' , 'raw' ) ;
73
+ $files . each ( function ( i , fileElm ) {
74
+ var trElm = fileElm . parentNode . parentNode ,
75
+ cntElm = trElm . querySelector ( '.content' ) ,
76
+ cntA = cntElm . querySelector ( 'a' ) ,
77
+ fileName = cntA . innerText ,
78
+ $a = $ ( '<a></a>' ) ;
79
+ $a . text ( '下载' ) ;
80
+ $a . attr ( { class :'fileDownLink' , 'download-url' :path + '/' + fileName , 'filename' :fileName } ) ;
81
+ $a . css ( { cursor :'pointer' , visibility :'hidden' } ) ;
82
+ cntElm . appendChild ( $a . get ( 0 ) ) ;
83
+ log . logObj ( 'tr' , trElm ) ;
84
+ trElm . onmouseover = mouseOverHandler ;
85
+ trElm . onmouseout = mouseOutHandler ;
86
+ $a . on ( 'click' , linkClick ) ;
87
+ } ) ;
88
+ $directory . each ( function ( i , dirElm ) {
89
+ /*var $directoryUrl = $('.js-navigation-open');
90
+ $directoryUrl.click(
91
+ function() {
92
+ console.log("当前URL为:", $(this).attr('href'));
93
+ }
94
+ );*/
95
+ //console.log("当前URL为:", $(this).attr('href'));
96
+ var trElm = dirElm . parentNode . parentNode ,
97
+ cntElm = trElm . querySelector ( '.content' ) ,
98
+ cntCssTruncate = cntElm . querySelector ( '.css-truncate.css-truncate-target' ) ,
99
+ cntCssTruncateA = cntCssTruncate . querySelector ( '.js-navigation-open' ) ,
100
+ cntA = cntElm . querySelector ( 'a' ) ,
101
+ fileName = cntA . innerText ,
102
+ dirUrl = cntCssTruncateA . href ,
103
+ $a = $ ( '<a></a>' ) ;
104
+
105
+ $a . text ( '下载' ) ;
106
+ $a . attr ( { class :'fileDownLink' , 'download-url' :path + '/' + fileName , 'filename' :fileName } ) ;
107
+ $a . css ( { cursor :'pointer' , visibility :'hidden' } ) ;
108
+ cntElm . appendChild ( $a . get ( 0 ) ) ;
109
+ log . logObj ( 'tr' , trElm ) ;
110
+ trElm . onmouseover = mouseOverHandler ;
111
+ trElm . onmouseout = mouseOutHandler ;
112
+ $a . on ( 'click' , function ( ) {
113
+ var downloadUrl = "https://minhaskamal.github.io/DownGit/#/home?url=" + dirUrl ;
114
+ window . open ( downloadUrl , "_blank" ) ;
115
+ } ) ;
116
+ } ) ;
117
+ }
118
+ /**
119
+ * issues页面从新标签打开
120
+ * @return {[type] } [description]
121
+ */
122
+ function openIssueFromNew ( ) {
123
+ var tmpArr = href . split ( '/' ) ;
124
+ if ( tmpArr [ tmpArr . length - 1 ] . indexOf ( 'issues' ) < 0 ) return ;
125
+ $ ( '.issues-listing .js-navigation-container a' ) . on ( 'click' , function ( e ) {
126
+ e . preventDefault ( ) ;
127
+ e . stopPropagation ( ) ;
128
+ log ( 'this' , this ) ;
129
+ window . open ( this . href ) ;
130
+ } ) ;
131
+ }
132
+ /**
133
+ * 创建复制链接
134
+ * @return {[type] } [description]
135
+ */
136
+ function createCopyLink ( ) {
137
+ //如果不是具体的文件页面则直接返回
138
+ var $btnGroup = $ ( '.file-actions .BtnGroup' ) ;
139
+ if ( $btnGroup . length == 0 ) return ;
140
+
141
+ var tmpArr = ___location . href . split ( '/' ) ;
142
+ tmpArr = tmpArr [ tmpArr . length - 1 ] . split ( '.' ) ; //获取扩展名
143
+ var excludeExts = [ 'jpg' , 'md' , 'markdown' , 'MD' , 'png' ] ;
144
+ if ( tmpArr . length > 1 && excludeExts . indexOf ( tmpArr [ 1 ] ) >= 0 ) return ;
145
+ var $a = $ ( '<a></a>' ) ;
146
+ $a . attr ( { href :'#' , class :'btn btn-sm BtnGroup-item copyButton' } ) ;
147
+ $a . html ( 'Copy' ) ;
148
+ $btnGroup . append ( $a ) ;
149
+ var addClickHandler = function ( ) {
150
+ timeout = null ;
151
+ var $codes = $ ( '.js-file-line-container .js-file-line' ) ,
152
+ text = '' ;
153
+ $codes . each ( function ( index , code ) {
154
+ log . logObj ( 'code' , code ) ;
155
+ text += code . innerText ;
156
+ if ( code . innerText . indexOf ( '\n' ) < 0 ) text += '\n' ;
157
+ } ) ;
158
+ $a . attr ( 'data-clipboard-text' , text ) ;
159
+ $a . on ( 'click' , function ( ) {
160
+ showTips ( 'Copy Success' ) ;
161
+ } ) ;
162
+ new Clipboard ( '.copyButton' ) ;
163
+ log . logObj ( 'text' , text ) ;
164
+ } ;
165
+ $a . one ( 'click' , function ( evt ) {
166
+ if ( timeout ) {
167
+ clearTimeout ( timeout ) ;
168
+ addClickHandler ( ) ;
169
+ }
170
+ $a . click ( ) ;
171
+ } ) ;
172
+ var timeout = setTimeout ( addClickHandler , 1000 ) ;
173
+ }
174
+ /**
175
+ * 点击图片处理函数
176
+ * @return {[type] } [description]
177
+ */
178
+ function bindImgClick ( ) {
179
+ var $imgs = $ ( 'article img' ) ;
180
+ var srcArr = [ ] ;
181
+ var newImg = null ;
182
+ var $modal = null ;
183
+ var width = $ ( window ) . width ( ) , height = $ ( window ) . height ( ) ;
184
+ //如果是issues页面,则改变img集合
185
+ var tmpArr = href . split ( '/' ) ;
186
+ if ( tmpArr [ tmpArr . length - 2 ] . indexOf ( 'issues' ) >= 0 ) {
187
+ $imgs = $ ( '#show_issue .comment img' ) ;
188
+ }
189
+ var newImgOnload = function ( ) {
190
+ var imgWidth = newImg . width , imgHeight = newImg . height ;
191
+ if ( imgWidth > width || imgHeight > height )
192
+ if ( height > width ) {
193
+ newImg . width = width ;
194
+ } else {
195
+ newImg . height = height ;
196
+ }
197
+ newImg . style . marginLeft = ( width - newImg . width ) / 2 + 'px' ;
198
+ newImg . style . marginTop = ( height - newImg . height ) / 2 + 'px' ;
199
+ } ;
200
+ var initModal = function ( ) {
201
+ $modal = $ ( '<div></div>' ) ;
202
+ newImg = new Image ( ) ;
203
+ $modal . css ( { position :'fixed' , width :width + 'px' , height :height + 'px' , 'background-color' :'rgba(0,0,0,0.5)' , top :0 , left :0 , 'z-index' :- 1 , 'padding-top' :0 , 'padding-left' :'auto' , visibility :'hidden' } ) ;
204
+ $modal . append ( newImg ) ;
205
+ $ ( 'body' ) . append ( $modal ) ;
206
+ $modal . on ( 'contextmenu' , function ( e ) {
207
+ $modal . css ( { 'z-index' :- 1 , 'visibility' :'hidden' } ) ;
208
+ return false ;
209
+ } ) ;
210
+ $modal . on ( 'click' , function ( e ) {
211
+ var mouseX = e . originalEvent . x || e . originalEvent . layerX || 0 ;
212
+ log . logObj ( 'mouseX' , mouseX ) ;
213
+ var oldSrc = newImg . src ;
214
+ var index = srcArr . indexOf ( oldSrc ) ;
215
+ if ( mouseX > width / 2 ) {
216
+ //当前src在数组中的位置
217
+ index = ++ index >= srcArr . length ? 0 : index ;
218
+ newImg . src = srcArr [ index ] ;
219
+ } else {
220
+ index = -- index < 0 ? srcArr . length - 1 : index ;
221
+ newImg . src = srcArr [ index ] ;
222
+ }
223
+ newImg . onload = newImgOnload ;
224
+ } ) ;
225
+ } ;
226
+ var imgClickHandler = function ( e ) {
227
+ log ( 'imgClickHandler' ) ;
228
+ if ( ! $modal ) initModal ( ) ;
229
+ $modal . css ( { visibility :'visible' , 'z-index' :999 , userSelect :'none' } ) ;
230
+ var oldImg = e . currentTarget ;
231
+ newImg . src = oldImg . src ;
232
+ //计算宽高
233
+ newImg . onload = newImgOnload ;
234
+ } ;
235
+ $imgs . each ( function ( i , img ) {
236
+ var aElm = img . parentNode ;
237
+ if ( aElm . getAttribute ( 'rel' ) !== 'noopener noreferrer' ) return ;
238
+ aElm . removeAttribute ( 'href' ) ;
239
+ var $img = $ ( img ) ;
240
+ $img . css ( 'cursor' , 'pointer' ) . on ( 'click' , imgClickHandler ) ;
241
+ //去重
242
+ let index = srcArr . indexOf ( img . src ) ;
243
+ if ( index < 0 ) srcArr . push ( img . src ) ;
244
+ } ) ;
245
+ }
246
+ /**
247
+ * 在Issue页面生成过滤面板
248
+ * @return {[type] } [description]
249
+ */
250
+ function createFilterPanel ( ) {
251
+ //如果不是具体issus页面,则直接退出函数
252
+ var tmpArr = href . split ( '/' ) ;
253
+ if ( tmpArr [ tmpArr . length - 2 ] . indexOf ( 'issues' ) < 0 ) return ;
254
+
255
+ var $panel = $ ( '.add-reactions-options.mx-1.mb-1' ) . eq ( 0 ) . clone ( true ) ;
256
+ $ ( '.discussion-sidebar-item.sidebar-assignee.js-discussion-sidebar-item' ) . prepend ( $panel ) ;
257
+ var $cancelBtn = $ ( '<button></button>' ) . text ( 'X' ) ;
258
+ $cancelBtn . get ( 0 ) . className = 'btn-link add-reactions-options-item js-reaction-option-item cancel-filter-btn' ;
259
+ $panel . append ( $cancelBtn ) ;
260
+ var $btns = $panel . find ( 'button' ) ;
261
+ var filterHandler = function ( evt ) {
262
+ var btn = evt . currentTarget ;
263
+ var val = btn . value ;
264
+ var className = btn . className ;
265
+ log ( 'value' , val ) ;
266
+ var $comments = $ ( '.timeline-comment-wrapper.js-comment-container' ) ;
267
+ var authors = [ ] ;
268
+ //显示全部
269
+ if ( className . indexOf ( 'cancel-filter-btn' ) >= 0 ) {
270
+ $comments . each ( function ( index , comment ) {
271
+ $comments . eq ( index ) . css ( 'display' , 'block' ) ;
272
+ } ) ;
273
+ return ;
274
+ }
275
+ //替换特殊情况
276
+ val . replace ( 'LAUGH unreact' , 'LAUGH react' ) ;
277
+ $comments . each ( function ( index , comment ) {
278
+ var $comment = $comments . eq ( index ) ;
279
+ var $sumBtns = $comment . find ( '.btn-link.reaction-summary-item' ) ;
280
+ $sumBtns . each ( function ( i , btn ) {
281
+ if ( btn . value === val ) {
282
+ authors . push ( $comment . find ( 'a.author' ) . text ( ) ) ;
283
+ }
284
+ } ) ;
285
+ } ) ;
286
+ $comments . each ( function ( index , comment ) {
287
+ var $comment = $comments . eq ( index ) ;
288
+ var authorName = $comment . find ( 'a.author' ) . text ( ) ;
289
+ if ( authors . indexOf ( authorName ) < 0 ) {
290
+ $comment . css ( 'display' , 'none' ) ;
291
+ } else {
292
+ $comment . css ( 'display' , 'block' ) ;
293
+ }
294
+ } ) ;
295
+ $comments . eq ( 0 ) . css ( 'display' , 'block' ) ;
296
+ } ;
297
+ $btns . each ( function ( index , elem ) {
298
+ elem . addEventListener ( 'click' , filterHandler ) ;
299
+ } ) ;
300
+ }
301
+ init ( ) ;
302
+ $ ( document ) . on ( 'pjax:success' , function ( evt ) {
303
+ log ( 'pjax:success' ) ;
304
+ init ( ) ;
305
+ } ) ;
306
+ } ) ( ) ;
0 commit comments