Skip to content

Commit 0ca66b1

Browse files
committed
Support to install from sub-folders, fixes #6
1 parent 7afce70 commit 0ca66b1

File tree

2 files changed

+103
-68
lines changed

2 files changed

+103
-68
lines changed

bootstrap.js

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/* ***** BEGIN LICENSE BLOCK *****
2-
*
2+
*
33
* This Source Code Form is subject to the terms of the Mozilla Public
44
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
55
* You can obtain one at http://mozilla.org/MPL/2.0/
6-
*
6+
*
77
* Contributor(s):
88
* Diego Casorran <dcasorran@gmail.com> (Original Author)
9-
*
9+
*
1010
* ***** END LICENSE BLOCK ***** */
1111

1212
let {classes:Cc,interfaces:Ci,utils:Cu,results:Cr} = Components, addon;
@@ -42,26 +42,26 @@ showAlertNotification = showAlertNotification.showAlertNotification.bind(showAle
4242

4343
function iNotify(aMsg, callback) {
4444
let nme = addon.branch.getIntPref('nme');
45-
45+
4646
if(nme == 2) {
4747
showAlertNotification(rsc("icon.png"),addon.name,aMsg,!1,"",
4848
(s,t) => t == "alertshow" || callback(t));
4949
} else {
5050
if(nme) Services.prompt.alert(null,addon.name,aMsg);
51-
51+
5252
callback();
5353
}
5454
}
5555

5656
function onClickHanlder(ev) {
5757
ev.preventDefault();
58-
58+
5959
if(this.hasAttribute(addon.tag)) {
6060
Services.prompt.alert(null,addon.name,
6161
"Don't click me more than once, reload the page to retry.");
6262
return;
6363
}
64-
64+
6565
this.setAttribute(addon.tag,1);
6666
this.className += ' danger disabled';
6767
let d = this.ownerDocument,
@@ -72,16 +72,16 @@ function onClickHanlder(ev) {
7272
d.body.appendChild(d.createElement('style')).textContent = '@keyframes '
7373
+addon.tag+'{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}';
7474
f.style.animation = addon.tag + ' 3s infinite linear';
75-
75+
7676
xhr(this.href || this.getAttribute('href'),data => {
7777
let iStream = Cc["@mozilla.org/io/arraybuffer-input-stream;1"]
7878
.createInstance(Ci.nsIArrayBufferInputStream);
79-
79+
8080
iStream.setData(data,0,data.byteLength);
81-
81+
8282
let nFile = FileUtils.getFile("TmpD", [Math.random()])
8383
oStream = FileUtils.openSafeFileOutputStream(nFile);
84-
84+
8585
NetUtil.asyncCopy(iStream, oStream, aStatus => {
8686
if(!Components.isSuccessCode(aStatus)) {
8787
Services.prompt.alert(null,addon.name,
@@ -91,37 +91,39 @@ function onClickHanlder(ev) {
9191
.createInstance(Ci.nsIZipReader),
9292
zipWriter = Cc["@mozilla.org/zipwriter;1"]
9393
.createInstance(Ci.nsIZipWriter);
94-
94+
9595
let oFile = FileUtils.getFile("TmpD", [addon.tag+'.xpi']);
9696
zipReader.open(nFile);
9797
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);
100102
while(m.hasMore()) {
101103
let f = m.getNext(),
102104
e = zipReader.getEntry(f);
103-
105+
104106
if(!(e instanceof Ci.nsIZipEntry))
105107
continue;
106-
107-
let n = (e.name||f).replace(/^[^\/]+\//,'');
108+
109+
let n = (e.name||f).replace(/^[^\/]+\//,'').replace(p,'');
108110
if(!n) continue;
109-
111+
110112
if(e.isDirectory) {
111-
113+
112114
zipWriter.addEntryDirectory(n,e.lastModifiedTime,!1);
113-
115+
114116
} else {
115-
117+
116118
zipWriter.addEntryStream(n, e.lastModifiedTime,
117119
Ci.nsIZipWriter.COMPRESSION_FASTEST,
118120
zipReader.getInputStream(f), !1);
119121
}
120122
}
121-
123+
122124
zipReader.close();
123125
zipWriter.close();
124-
126+
125127
AddonManager.getInstallForFile(oFile,aInstall => {
126128
let done = (aMsg) => {
127129
let c = 'check';
@@ -137,61 +139,78 @@ function onClickHanlder(ev) {
137139
f.className = f.className.replace('hourglass',c);
138140
iNotify(aMsg, () => oFile.remove(!1));
139141
};
140-
142+
141143
aInstall.addListener({
142144
onInstallFailed : function(aInstall) {
143145
aInstall.removeListener(this);
144-
146+
145147
done(aInstall.error);
146148
},
147149
onInstallEnded : function(aInstall,aAddon) {
148150
aInstall.removeListener(this);
149-
151+
150152
done(aAddon.name + ' ' + aAddon.version
151153
+ ' has been installed successfully.');
152154
}
153155
});
154156
aInstall.install();
155157
});
156-
158+
157159
nFile.remove(!1);
158160
}
159161
});
160162
});
161163
}
162164

163165
function addButton(n,u) {
166+
if([n.nextElementSibling,n.previousElementSibling]
167+
.some(e=>e&&~e.className.indexOf(addon.tag)))
168+
return;
169+
164170
let p = n.parentNode;
165171
n = n.cloneNode(!0);
166-
167-
n.id = addon.tag;
172+
173+
n.className += ' ' + addon.tag;
168174
n.title = 'Install Extension';
169175
n.textContent = ' Add to ' + Services.appinfo.name;
170176
n.insertBefore(n.ownerDocument.createElement('span'),
171177
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+
174185
n.addEventListener('click', onClickHanlder, false);
175-
186+
176187
if(u) {
177-
n.setAttribute('href',u);
188+
let b = n.ownerDocument.querySelector('div.breadcrumb');
189+
190+
n.setAttribute('href', u );
178191
n.style.cursor = 'pointer';
179192
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+
}
181202
}
182203
}
183204

184205
function onPageLoad(doc) {
185-
if(doc.getElementById(addon.tag)) return;
186-
187206
if(doc.location.pathname.replace(/\/[^/]+$/,'').substr(-4) === 'pull') {
188207
// Based on work by Jerone: https://github.com/jerone/UserScripts
189-
208+
190209
let r = '' + doc.location.pathname.split('/').filter(String).slice(1,2),
191210
v = addon.branch.getPrefType('prs') && addon.branch.getCharPref('prs') || '';
192-
211+
193212
if(~v.toLowerCase().split(',').indexOf(r.toLowerCase())) {
194-
213+
195214
let n = doc.querySelectorAll('span.commit-ref.current-branch.css-truncate.js-selectable-text.expandable')[1],
196215
b = n.textContent.trim().split(':'),
197216
t = b.shift(),
@@ -200,19 +219,35 @@ function onPageLoad(doc) {
200219
doc.querySelector('.js-current-repository').textContent,
201220
'archive', b.join(':') + '.zip'
202221
].join('/');
203-
222+
204223
addButton(n,u);
205224
}
206225
}
207226
else if([].some.call(doc.querySelectorAll('table.files > tbody > tr > td.content'),
208227
(n) => 'install.rdf' === n.textContent.trim())) {
209-
210-
let c = 7, n;
228+
229+
let c = 7, n, z;
211230
while(c-- && !(n=doc.querySelector('a.minibutton:nth-child('+c+')')));
212-
231+
213232
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+
}
216251
}
217252
}
218253
}
@@ -221,7 +256,7 @@ function loadIntoWindow(window) {
221256
if(window.document.documentElement
222257
.getAttribute("windowtype") != 'navigator:browser')
223258
return;
224-
259+
225260
function onMutation(ms,doc) {
226261
for(let m of ms) {
227262
if('class' == m.attributeName) {
@@ -233,22 +268,22 @@ function loadIntoWindow(window) {
233268
}
234269
}
235270
}
236-
271+
237272
let domload = ev => {
238273
let doc = ev.originalTarget;
239-
274+
240275
if(!(doc.location && doc.location.host == 'github.com'))
241276
return;
242-
277+
243278
['page-context-loader','context-loader'].forEach(e => {
244-
279+
245280
e = doc.getElementsByClassName(e);
246281
for(let o of e) {
247282
new doc.defaultView.MutationObserver(m => onMutation(m,doc))
248283
.observe(o,{attributes:!0,attributeOldValue:!0});
249284
}
250285
});
251-
286+
252287
onPageLoad(doc);
253288
};
254289
getBrowser(window).addEventListener('DOMContentLoaded', domload, false);
@@ -258,7 +293,7 @@ function loadIntoWindow(window) {
258293
function xhr(url,cb) {
259294
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
260295
.createInstance(Ci.nsIXMLHttpRequest);
261-
296+
262297
let handler = ev => {
263298
evf(m => xhr.removeEventListener(m,handler,!1));
264299
switch(ev.type) {
@@ -274,10 +309,10 @@ function xhr(url,cb) {
274309
break;
275310
}
276311
};
277-
312+
278313
let evf = f => ['load','error','abort'].forEach(f);
279314
evf(m => xhr.addEventListener( m, handler, false));
280-
315+
281316
xhr.mozBackgroundRequest = true;
282317
xhr.open('GET', url, true);
283318
xhr.channel.loadFlags |=
@@ -289,18 +324,18 @@ function xhr(url,cb) {
289324
}
290325

291326
function getBrowser(w) {
292-
327+
293328
if(typeof w.getBrowser === 'function')
294329
return w.getBrowser();
295-
330+
296331
if("gBrowser" in w)
297332
return w.gBrowser;
298-
333+
299334
return w.BrowserApp.deck;
300335
}
301336

302337
function loadIntoWindowStub(domWindow) {
303-
338+
304339
if(domWindow.document.readyState == "complete") {
305340
loadIntoWindow(domWindow);
306341
} else {
@@ -330,16 +365,16 @@ function startup(data) {
330365
wms: new WeakMap()
331366
};
332367
addon.branch = Services.prefs.getBranch('extensions.'+addon.tag+'.');
333-
368+
334369
let io = Services.io;
335370
io.getProtocolHandler("resource")
336371
.QueryInterface(Ci.nsIResProtocolHandler)
337372
.setSubstitution(addon.tag,
338373
io.newURI(__SCRIPT_URI_SPEC__+'/../',null,null));
339-
374+
340375
i$.wmf(loadIntoWindowStub);
341376
Services.wm.addListener(i$);
342-
377+
343378
if(!addon.branch.getPrefType('nme')) {
344379
addon.branch.setIntPref('nme',2);
345380
}
@@ -350,10 +385,10 @@ function startup(data) {
350385
function shutdown(data, reason) {
351386
if(reason == APP_SHUTDOWN)
352387
return;
353-
388+
354389
Services.wm.removeListener(i$);
355390
i$.wmf(unloadFromWindow);
356-
391+
357392
Services.io.getProtocolHandler("resource")
358393
.QueryInterface(Ci.nsIResProtocolHandler)
359394
.setSubstitution(addon.tag,null);

0 commit comments

Comments
 (0)