1
1
/* ***** BEGIN LICENSE BLOCK *****
2
- *
2
+ *
3
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
4
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
5
* You can obtain one at http://mozilla.org/MPL/2.0/
6
- *
6
+ *
7
7
* Contributor(s):
8
8
* Diego Casorran <dcasorran@gmail.com> (Original Author)
9
- *
9
+ *
10
10
* ***** END LICENSE BLOCK ***** */
11
11
12
12
let { classes :Cc , interfaces :Ci , utils :Cu , results :Cr } = Components , addon ;
@@ -42,26 +42,26 @@ showAlertNotification = showAlertNotification.showAlertNotification.bind(showAle
42
42
43
43
function iNotify ( aMsg , callback ) {
44
44
let nme = addon . branch . getIntPref ( 'nme' ) ;
45
-
45
+
46
46
if ( nme == 2 ) {
47
47
showAlertNotification ( rsc ( "icon.png" ) , addon . name , aMsg , ! 1 , "" ,
48
48
( s , t ) => t == "alertshow" || callback ( t ) ) ;
49
49
} else {
50
50
if ( nme ) Services . prompt . alert ( null , addon . name , aMsg ) ;
51
-
51
+
52
52
callback ( ) ;
53
53
}
54
54
}
55
55
56
56
function onClickHanlder ( ev ) {
57
57
ev . preventDefault ( ) ;
58
-
58
+
59
59
if ( this . hasAttribute ( addon . tag ) ) {
60
60
Services . prompt . alert ( null , addon . name ,
61
61
"Don't click me more than once, reload the page to retry." ) ;
62
62
return ;
63
63
}
64
-
64
+
65
65
this . setAttribute ( addon . tag , 1 ) ;
66
66
this . className += ' danger disabled' ;
67
67
let d = this . ownerDocument ,
@@ -72,16 +72,16 @@ function onClickHanlder(ev) {
72
72
d . body . appendChild ( d . createElement ( 'style' ) ) . textContent = '@keyframes '
73
73
+ addon . tag + '{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}' ;
74
74
f . style . animation = addon . tag + ' 3s infinite linear' ;
75
-
75
+
76
76
xhr ( this . href || this . getAttribute ( 'href' ) , data => {
77
77
let iStream = Cc [ "@mozilla.org/io/arraybuffer-input-stream;1" ]
78
78
. createInstance ( Ci . nsIArrayBufferInputStream ) ;
79
-
79
+
80
80
iStream . setData ( data , 0 , data . byteLength ) ;
81
-
81
+
82
82
let nFile = FileUtils . getFile ( "TmpD" , [ Math . random ( ) ] )
83
83
oStream = FileUtils . openSafeFileOutputStream ( nFile ) ;
84
-
84
+
85
85
NetUtil . asyncCopy ( iStream , oStream , aStatus => {
86
86
if ( ! Components . isSuccessCode ( aStatus ) ) {
87
87
Services . prompt . alert ( null , addon . name ,
@@ -91,37 +91,39 @@ function onClickHanlder(ev) {
91
91
. createInstance ( Ci . nsIZipReader ) ,
92
92
zipWriter = Cc [ "@mozilla.org/zipwriter;1" ]
93
93
. createInstance ( Ci . nsIZipWriter ) ;
94
-
94
+
95
95
let oFile = FileUtils . getFile ( "TmpD" , [ addon . tag + '.xpi' ] ) ;
96
96
zipReader . open ( nFile ) ;
97
97
zipWriter . open ( oFile , 0x2c ) ;
98
-
99
- let m = zipReader . findEntries ( "*/*" ) ;
98
+
99
+ let p = ( this . getAttribute ( 'path' ) || "*/" ) ,
100
+ m = zipReader . findEntries ( p + "*" ) ;
101
+ p = p . substr ( 2 ) ;
100
102
while ( m . hasMore ( ) ) {
101
103
let f = m . getNext ( ) ,
102
104
e = zipReader . getEntry ( f ) ;
103
-
105
+
104
106
if ( ! ( e instanceof Ci . nsIZipEntry ) )
105
107
continue ;
106
-
107
- let n = ( e . name || f ) . replace ( / ^ [ ^ \/ ] + \/ / , '' ) ;
108
+
109
+ let n = ( e . name || f ) . replace ( / ^ [ ^ \/ ] + \/ / , '' ) . replace ( p , '' ) ;
108
110
if ( ! n ) continue ;
109
-
111
+
110
112
if ( e . isDirectory ) {
111
-
113
+
112
114
zipWriter . addEntryDirectory ( n , e . lastModifiedTime , ! 1 ) ;
113
-
115
+
114
116
} else {
115
-
117
+
116
118
zipWriter . addEntryStream ( n , e . lastModifiedTime ,
117
119
Ci . nsIZipWriter . COMPRESSION_FASTEST ,
118
120
zipReader . getInputStream ( f ) , ! 1 ) ;
119
121
}
120
122
}
121
-
123
+
122
124
zipReader . close ( ) ;
123
125
zipWriter . close ( ) ;
124
-
126
+
125
127
AddonManager . getInstallForFile ( oFile , aInstall => {
126
128
let done = ( aMsg ) => {
127
129
let c = 'check' ;
@@ -137,61 +139,78 @@ function onClickHanlder(ev) {
137
139
f . className = f . className . replace ( 'hourglass' , c ) ;
138
140
iNotify ( aMsg , ( ) => oFile . remove ( ! 1 ) ) ;
139
141
} ;
140
-
142
+
141
143
aInstall . addListener ( {
142
144
onInstallFailed : function ( aInstall ) {
143
145
aInstall . removeListener ( this ) ;
144
-
146
+
145
147
done ( aInstall . error ) ;
146
148
} ,
147
149
onInstallEnded : function ( aInstall , aAddon ) {
148
150
aInstall . removeListener ( this ) ;
149
-
151
+
150
152
done ( aAddon . name + ' ' + aAddon . version
151
153
+ ' has been installed successfully.' ) ;
152
154
}
153
155
} ) ;
154
156
aInstall . install ( ) ;
155
157
} ) ;
156
-
158
+
157
159
nFile . remove ( ! 1 ) ;
158
160
}
159
161
} ) ;
160
162
} ) ;
161
163
}
162
164
163
165
function addButton ( n , u ) {
166
+ if ( [ n . nextElementSibling , n . previousElementSibling ]
167
+ . some ( e => e && ~ e . className . indexOf ( addon . tag ) ) )
168
+ return ;
169
+
164
170
let p = n . parentNode ;
165
171
n = n . cloneNode ( ! 0 ) ;
166
-
167
- n . id = addon . tag ;
172
+
173
+ n . className += ' ' + addon . tag ;
168
174
n . title = 'Install Extension' ;
169
175
n . textContent = ' Add to ' + Services . appinfo . name ;
170
176
n . insertBefore ( n . ownerDocument . createElement ( 'span' ) ,
171
177
n . firstChild ) . className = 'octicon octicon-plus' ;
172
- p . appendChild ( n ) ;
173
-
178
+
179
+ if ( typeof u !== 'object' ) {
180
+ p . appendChild ( n ) ;
181
+ } else {
182
+ p . insertBefore ( n , p . firstElementChild ) ;
183
+ }
184
+
174
185
n . addEventListener ( 'click' , onClickHanlder , false ) ;
175
-
186
+
176
187
if ( u ) {
177
- n . setAttribute ( 'href' , u ) ;
188
+ let b = n . ownerDocument . querySelector ( 'div.breadcrumb' ) ;
189
+
190
+ n . setAttribute ( 'href' , u ) ;
178
191
n . style . cursor = 'pointer' ;
179
192
n . style . setProperty ( 'box-shadow' , 'none' , 'important' ) ;
180
- n . className += ' button primary pseudo-class-active' ;
193
+ n . setAttribute ( 'path' , b && b . textContent
194
+ . replace ( " " , '' , 'g' ) . replace ( / ^ [ ^ / ] + / , '*' ) || '' ) ;
195
+
196
+ if ( typeof u !== 'object' ) {
197
+ n . className += ' button primary pseudo-class-active' ;
198
+ } else {
199
+ n . className = 'minibutton pseudo-class-active' ;
200
+ n . firstChild . style . verticalAlign = 'baseline' ;
201
+ }
181
202
}
182
203
}
183
204
184
205
function onPageLoad ( doc ) {
185
- if ( doc . getElementById ( addon . tag ) ) return ;
186
-
187
206
if ( doc . location . pathname . replace ( / \/ [ ^ / ] + $ / , '' ) . substr ( - 4 ) === 'pull' ) {
188
207
// Based on work by Jerone: https://github.com/jerone/UserScripts
189
-
208
+
190
209
let r = '' + doc . location . pathname . split ( '/' ) . filter ( String ) . slice ( 1 , 2 ) ,
191
210
v = addon . branch . getPrefType ( 'prs' ) && addon . branch . getCharPref ( 'prs' ) || '' ;
192
-
211
+
193
212
if ( ~ v . toLowerCase ( ) . split ( ',' ) . indexOf ( r . toLowerCase ( ) ) ) {
194
-
213
+
195
214
let n = doc . querySelectorAll ( 'span.commit-ref.current-branch.css-truncate.js-selectable-text.expandable' ) [ 1 ] ,
196
215
b = n . textContent . trim ( ) . split ( ':' ) ,
197
216
t = b . shift ( ) ,
@@ -200,19 +219,35 @@ function onPageLoad(doc) {
200
219
doc . querySelector ( '.js-current-repository' ) . textContent ,
201
220
'archive' , b . join ( ':' ) + '.zip'
202
221
] . join ( '/' ) ;
203
-
222
+
204
223
addButton ( n , u ) ;
205
224
}
206
225
}
207
226
else if ( [ ] . some . call ( doc . querySelectorAll ( 'table.files > tbody > tr > td.content' ) ,
208
227
( n ) => 'install.rdf' === n . textContent . trim ( ) ) ) {
209
-
210
- let c = 7 , n ;
228
+
229
+ let c = 7 , n , z ;
211
230
while ( c -- && ! ( n = doc . querySelector ( 'a.minibutton:nth-child(' + c + ')' ) ) ) ;
212
-
231
+
213
232
if ( n && n . textContent . trim ( ) === 'Download ZIP' ) {
214
-
215
- addButton ( n ) ;
233
+ c = doc . querySelector ( 'div.only-with-full-nav' ) ;
234
+
235
+ if ( ! c || doc . defaultView . getComputedStyle ( c ) . getPropertyValue ( 'display' ) == 'block' ) {
236
+ addButton ( n ) ;
237
+ } else {
238
+ z = n ;
239
+ n = 0 ;
240
+ }
241
+ }
242
+
243
+ if ( ! n ) {
244
+ n = doc . querySelector ( 'div.file-navigation' ) ;
245
+ n = n && n . firstElementChild ;
246
+
247
+ if ( n ) {
248
+
249
+ addButton ( n , z ) ;
250
+ }
216
251
}
217
252
}
218
253
}
@@ -221,7 +256,7 @@ function loadIntoWindow(window) {
221
256
if ( window . document . documentElement
222
257
. getAttribute ( "windowtype" ) != 'navigator:browser' )
223
258
return ;
224
-
259
+
225
260
function onMutation ( ms , doc ) {
226
261
for ( let m of ms ) {
227
262
if ( 'class' == m . attributeName ) {
@@ -233,22 +268,22 @@ function loadIntoWindow(window) {
233
268
}
234
269
}
235
270
}
236
-
271
+
237
272
let domload = ev => {
238
273
let doc = ev . originalTarget ;
239
-
274
+
240
275
if ( ! ( doc . location && doc . location . host == 'github.com' ) )
241
276
return ;
242
-
277
+
243
278
[ 'page-context-loader' , 'context-loader' ] . forEach ( e => {
244
-
279
+
245
280
e = doc . getElementsByClassName ( e ) ;
246
281
for ( let o of e ) {
247
282
new doc . defaultView . MutationObserver ( m => onMutation ( m , doc ) )
248
283
. observe ( o , { attributes :! 0 , attributeOldValue :! 0 } ) ;
249
284
}
250
285
} ) ;
251
-
286
+
252
287
onPageLoad ( doc ) ;
253
288
} ;
254
289
getBrowser ( window ) . addEventListener ( 'DOMContentLoaded' , domload , false ) ;
@@ -258,7 +293,7 @@ function loadIntoWindow(window) {
258
293
function xhr ( url , cb ) {
259
294
let xhr = Cc [ "@mozilla.org/xmlextras/xmlhttprequest;1" ]
260
295
. createInstance ( Ci . nsIXMLHttpRequest ) ;
261
-
296
+
262
297
let handler = ev => {
263
298
evf ( m => xhr . removeEventListener ( m , handler , ! 1 ) ) ;
264
299
switch ( ev . type ) {
@@ -274,10 +309,10 @@ function xhr(url,cb) {
274
309
break ;
275
310
}
276
311
} ;
277
-
312
+
278
313
let evf = f => [ 'load' , 'error' , 'abort' ] . forEach ( f ) ;
279
314
evf ( m => xhr . addEventListener ( m , handler , false ) ) ;
280
-
315
+
281
316
xhr . mozBackgroundRequest = true ;
282
317
xhr . open ( 'GET' , url , true ) ;
283
318
xhr . channel . loadFlags |=
@@ -289,18 +324,18 @@ function xhr(url,cb) {
289
324
}
290
325
291
326
function getBrowser ( w ) {
292
-
327
+
293
328
if ( typeof w . getBrowser === 'function' )
294
329
return w . getBrowser ( ) ;
295
-
330
+
296
331
if ( "gBrowser" in w )
297
332
return w . gBrowser ;
298
-
333
+
299
334
return w . BrowserApp . deck ;
300
335
}
301
336
302
337
function loadIntoWindowStub ( domWindow ) {
303
-
338
+
304
339
if ( domWindow . document . readyState == "complete" ) {
305
340
loadIntoWindow ( domWindow ) ;
306
341
} else {
@@ -330,16 +365,16 @@ function startup(data) {
330
365
wms : new WeakMap ( )
331
366
} ;
332
367
addon . branch = Services . prefs . getBranch ( 'extensions.' + addon . tag + '.' ) ;
333
-
368
+
334
369
let io = Services . io ;
335
370
io . getProtocolHandler ( "resource" )
336
371
. QueryInterface ( Ci . nsIResProtocolHandler )
337
372
. setSubstitution ( addon . tag ,
338
373
io . newURI ( __SCRIPT_URI_SPEC__ + '/../' , null , null ) ) ;
339
-
374
+
340
375
i$ . wmf ( loadIntoWindowStub ) ;
341
376
Services . wm . addListener ( i$ ) ;
342
-
377
+
343
378
if ( ! addon . branch . getPrefType ( 'nme' ) ) {
344
379
addon . branch . setIntPref ( 'nme' , 2 ) ;
345
380
}
@@ -350,10 +385,10 @@ function startup(data) {
350
385
function shutdown ( data , reason ) {
351
386
if ( reason == APP_SHUTDOWN )
352
387
return ;
353
-
388
+
354
389
Services . wm . removeListener ( i$ ) ;
355
390
i$ . wmf ( unloadFromWindow ) ;
356
-
391
+
357
392
Services . io . getProtocolHandler ( "resource" )
358
393
. QueryInterface ( Ci . nsIResProtocolHandler )
359
394
. setSubstitution ( addon . tag , null ) ;
0 commit comments