From e6cf211934e7c88fc8f12b777fd41fd29406d5db Mon Sep 17 00:00:00 2001 From: KinoLien Date: Sun, 22 May 2022 17:15:59 +0800 Subject: [PATCH] Update and test 1.0.0 --- css/main.css | 92 ++- css/popup.css | 31 +- images/icon-128px.png | Bin 8970 -> 2533 bytes images/icon-empty.png | Bin 919 -> 0 bytes js/background.js | 178 +++--- js/content/main.js | 850 +++++++++++++++++++------ js/popup.js | 31 + manifest.json | 9 +- options.html | 1 + static/js/main.d164c8cf.js | 3 + static/js/main.d164c8cf.js.LICENSE.txt | 58 ++ static/js/main.d164c8cf.js.map | 1 + 12 files changed, 933 insertions(+), 321 deletions(-) delete mode 100644 images/icon-empty.png mode change 100644 => 100755 js/content/main.js mode change 100644 => 100755 manifest.json create mode 100644 options.html create mode 100644 static/js/main.d164c8cf.js create mode 100644 static/js/main.d164c8cf.js.LICENSE.txt create mode 100644 static/js/main.d164c8cf.js.map diff --git a/css/main.css b/css/main.css index d10a5a4..ad56ac2 100644 --- a/css/main.css +++ b/css/main.css @@ -10,8 +10,8 @@ } *[class*="gitzip-"]{ - transition:all 0.5s ease; - -webkit-transition:all 0.5s ease; /* Safari and Chrome */ + transition:all 0.3s ease; + -webkit-transition:all 0.3s ease; /* Safari and Chrome */ } .gitzip-show{ @@ -34,15 +34,43 @@ border-bottom: 1px solid #222; } +.gitzip-close{ + float: right; + font-weight: 800; + cursor: pointer; + visibility: hidden; +} + +.gitzip-fail .gitzip-close{ + visibility: visible; +} + .gitzip-body{ - margin: 15px; - padding: 0px; + margin: 10px 0; height: 246px; overflow: auto; } -.gitzip-dark .gitzip-body{ - color: #b3b3b3; +.gitzip-body p{ + margin: 0; + padding: 0 15px; + color: #333; +} + +.gitzip-dark .gitzip-body p{ + color: #f0f6fc; +} + +.gitzip-body p.warn{ + background-color: #fffbe7; + color: #624115; + border: 1px solid #fff4c7; +} + +.gitzip-body p.error{ + background-color: #fff0f0; + color: #ff081e; + border: 1px solid #ffd6d6; } .gitzip-dark .gitzip-body p.warn, .gitzip-dark .gitzip-body p.error{ @@ -63,30 +91,60 @@ .gitzip-check-mark{ position: absolute; - left: -10px; + left: -10px; top: 0px; font-size: 18px; margin:0 !important; visibility: hidden; opacity: 0; + color: #333; +} + +.gitzip-dark.gitzip-check-mark{ + color: #f0f6fc; +} + +.gitzip-check-wrap{ + position: absolute; + left: -5px; + top: 0px; + height: 100%; + display: none; + align-items: center; } .gitzip-collect-wrap{ position: fixed; right: 28px; bottom: 28px; + z-index: 1000; +} + +.gitzip-collect-arrow{ + position: absolute; + right: 0px; + bottom: 0px; visibility: hidden; opacity: 0; - z-index: 1000; width: 56px; height: 56px; + color: #333; } -.gitzip-collect-wrap:hover{ +.gitzip-dark .gitzip-collect-arrow{ + color: #f0f6fc; +} + +.gitzip-collect-arrow:hover{ width: 270px; height: 65px; border-radius: 8px; - background-color: black; + color: #f0f6fc; + background-color: #333; +} + +.gitzip-dark .gitzip-collect-arrow:hover{ + background-color: transparent; } .gitzip-collect-down{ @@ -101,18 +159,18 @@ bottom: 4px; } -.gitzip-collect-wrap:hover .gitzip-collect-down{ - color: #fff; -} - .gitzip-collect-dash{ box-shadow: 1px 1px 10px 0px #d0d0d0; background-color: #fff; border-radius: 4px; border: 1px solid #ddd; - display: none; visibility: hidden; opacity: 0; + position: absolute; + right: 0; + bottom: 0; + width: 0px; + height: 0px; } .gitzip-dark .gitzip-collect-dash { @@ -122,6 +180,7 @@ } .gitzip-downloading .gitzip-collect-dash{ + width: 100%; height: 100%; display: block; visibility: visible; @@ -142,9 +201,8 @@ padding-left:10px; } -.gitzip-collect-wrap:hover .gitzip-collect-tip{ +.gitzip-collect-arrow:hover .gitzip-collect-tip{ visibility: visible; opacity: 1; - color: #fff; left: 0px; } diff --git a/css/popup.css b/css/popup.css index 4445f63..964121b 100644 --- a/css/popup.css +++ b/css/popup.css @@ -18,13 +18,14 @@ body{ font-size: 14px; line-height: 1.42857143; color: #333; + margin: 5px; + background-color: #f5f5f5; } .panel{ width: 400px; height: 130px; background-color: #fff; - border: 1px solid transparent; border-radius: 4px; } @@ -70,3 +71,31 @@ body{ padding: 4px 5px 0px; display: none; } + +body.dark-theme { + color: #f0f6fc; + background-color: #1e2126; +} + +.dark-theme .panel-heading { + color: #f0f6fc; + background-color: #1e2126; + border-color: #222; +} + +.dark-theme .panel{ + background-color: #000; +} + +.dark-theme .form-control{ + outline: none !important; + color: #aaa; + background-color: #000; + border: 1px solid #222; + -webkit-box-shadow: inset 0 1px 1px rgba(255,255,255,.075); + box-shadow: inset 0 1px 1px rgba(255,255,255,.075); +} + +.dark-theme .form-control:focus{ + border: 1px solid #555; +} diff --git a/images/icon-128px.png b/images/icon-128px.png index 6954ef154f2400b8735fd50458b1e4994d9710d4..529202810740e8fd1c08b5cde5d17c5aa64574c6 100644 GIT binary patch delta 2511 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 diff --git a/images/icon-empty.png b/images/icon-empty.png deleted file mode 100644 index 663ddbbcdf716d2c82f8f487210fb8f879d76284..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 919 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCjKy@t)48K6?UobF~8Za=tN?>5Hn!&&zUNC1@pbb!hv%n*=n1O+{6NDLaGv)$S zGoH>2i71Ki^|4CM&(%vz$xlkvtH>8?tx|+V3m}@nzJge0 z12W3WzbG>m=v n.toLowerCase()); - if ( matches = theme.colors.icons.match(/rgb(a)?\((.*)\)/) ) { - var colorSets = matches.pop().split(",").map(function(str){ return str.trim(); }); - return colorSets.slice(0, 3).map(function(str){ return parseInt(str); }).concat([parseFloat(colorSets[3]) || 1]); - } else if ( (nameIdx = colorLowerNames.indexOf(theme.colors.icons.toLowerCase())) != -1 ) { - return convertHexToRgba(colorValues[nameIdx]); - } else if ( matches = theme.colors.icons.match(colorValRe) ) { - return convertHexToRgba(matches.pop()); - } - } - // default use #bababa - return convertHexToRgba("bababa"); -} - -function dynamicChangeColor(tabId) { - var canvas = document.createElement("canvas"); - var ctx = canvas.getContext("2d"); - - var poolimg = new Image(); - - poolimg.onload = function () { - //draw background image - ctx.drawImage(poolimg, 0, 0); - - var imageData = ctx.getImageData(0, 0, 128, 128); - - var outputData = ctx.createImageData(128, 128); - - browser.theme.getCurrent() - .then(function(theme){ - var colorSets = getValidThemeColorSet(theme); - for (var i = 0; i < imageData.data.length; i += 4) { - outputData.data[i + 0] = colorSets[0]; - outputData.data[i + 1] = colorSets[1]; - outputData.data[i + 2] = colorSets[2]; - outputData.data[i + 3] = Math.floor(imageData.data[i + 3] * colorSets[3]); - } - browser.pageAction.setIcon({ - tabId: tabId, - imageData: outputData - }); - browser.pageAction.show(tabId); - }); - }; - - poolimg.src = browser.runtime.getURL("images/icon-128px.png"); -} // Received a message from content script -browser.runtime.onMessage.addListener(function(request, sender, sendResponse) { +browser.runtime.onMessage.addListener(function(request, sender) { switch (request.action){ case "showIcon": - dynamicChangeColor(sender.tab.id); - return true; + return browser.pageAction.show(sender.tab.id); case "getKey": return browser.storage.sync.get("gitzip-github-token") .then(function(res){ return res["gitzip-github-token"] || ""; }); case "setKey": return browser.storage.sync.set( {"gitzip-github-token": request.value} ); - // case "gaTrack": - // var eventGaObj = { - // hitType: 'event', - // eventCategory: request.baseRepo, // /author/repo/ - // eventAction: request.userAction, - // eventLabel: request.githubUrl - // }; - // ga('send', eventGaObj); - // return; + case "createContextNested": + var main = browser.contextMenus.create({ + id: "gitzip-nested", + title: "GitZip Download", + contexts: ["all"] + }); + browser.contextMenus.create({ + id: "gitzip-nested-items", + parentId: main, + title: "Checked Items", + contexts: ["all"], + enabled: false + }); + browser.contextMenus.create({ + id: "gitzip-nested-seperator", + parentId: main, + title: "seperator", + type: "separator", + contexts: ["all"] + }); + browser.contextMenus.create({ + id: "gitzip-nested-selected", + parentId: main, + title: "(selected)", + contexts: ["all"], + enabled: false + }); + browser.contextMenus.create({ + id: "gitzip-nested-current", + parentId: main, + title: "(current)", + contexts: ["all"], + enabled: false + }); + return false; + case "updateContextNested": + var id = request.target; + var updateObj = { enabled: request.enabled !== false }; + switch (id) { + case "selected": + updateObj.title = updateObj.enabled ? ("Selected " + (request.urlType == "blob" ? "File - " : "Folder - ") + request.urlName) : "(selected)"; + break; + case "current": + if (request.root === true) + updateObj.title = "Whole Repository"; + else + updateObj.title = updateObj.enabled ? ("Current " + (request.urlType == "blob" ? "File - " : "Folder - ") + request.urlName) : "(current)"; + break; + } + return browser.contextMenus.update("gitzip-nested-" + id, updateObj); + case "removeContext": + return browser.contextMenus.removeAll(); + } +}); + +browser.contextMenus.onClicked.addListener(function(info, tab){ + if ( info.menuItemId.toString().indexOf("gitzip-") != -1 ) { + browser.tabs.sendMessage(tab.id, {action: info.menuItemId + "-clicked"}); } }); + +browser.tabs.onActivated.addListener(function(activeInfo) { + // handle other tabs active + browser.contextMenus.removeAll(); + + // change back to current tab + browser.tabs.sendMessage(activeInfo.tabId, {action: "github-tab-active", from: "onActivated" }); +}); + +browser.tabs.onUpdated.addListener(function(tabId, changeInfo){ + if ( changeInfo.status == "loading" ) { + browser.contextMenus.removeAll(); + } else if ( changeInfo.status == "complete" ) { + // coding like this because it would cause error during current page loading and then shift another tab quickly. + browser.tabs.query({currentWindow: true, active: true}, function(tabs){ + var tab = tabs[0]; + if(tab) browser.tabs.sendMessage(tab.id, {action: "github-tab-active", from: "onUpdated" }); + }); + } +}); + diff --git a/js/content/main.js b/js/content/main.js old mode 100644 new mode 100755 index a95791c..1234725 --- a/js/content/main.js +++ b/js/content/main.js @@ -1,11 +1,22 @@ // It would work on github.com - var repoExp = new RegExp("^https://github.com/([^/]+)/([^/]+)(/(tree|blob)/([^/]+)(/(.*))?)?"); +// For the request callback end point detection +var repoCommitExp = new RegExp("^https://github.com/([^/]+)/([^/]+)/commit/.*"); -function showGitzipIcon(){ - // just show the icon - browser.runtime.sendMessage({action: "showIcon"}); -} +var isStorageCallback = false; + +var isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + +// default +// var selectBehaviour = "both"; +var isOnlyDoubleClick = false; +var isOnlySingleCheck = false; +var isBoth = true; + +const defaultOptions = { + selectBehaviour: 'both', + theme: 'default' +}; /** * Resolve the github repo url for recognize author, project name, branch name, and so on. @@ -14,12 +25,18 @@ function showGitzipIcon(){ * @param {ResolvedURL} */ function resolveUrl(repoUrl){ - if(typeof repoUrl != 'string') return; + if(typeof repoUrl != 'string') return false; var matches = repoUrl.match(repoExp); if(matches && matches.length > 0){ - var root = (matches[5])? + var rootUrl = (matches[5])? "https://github.com/" + matches[1] + "/" + matches[2] + "/tree/" + matches[5] : - repoUrl; + "https://github.com/" + matches[1] + "/" + matches[2]; + + var strType = matches[4]; + if ( !strType && (repoUrl.length - rootUrl.length > 1) ) { // means no type and url different with root + return false; + } + return { author: matches[1], project: matches[2], @@ -27,9 +44,10 @@ function resolveUrl(repoUrl){ type: matches[4], path: matches[7] || '', inputUrl: repoUrl, - rootUrl: root + rootUrl: rootUrl }; } + return false; } // https://api.github.com/repos/peers/peerjs/git/trees/bfd406219ffd35f4ad870638f2180b27b4e9c374 @@ -46,16 +64,36 @@ function getInfoUrl(author, project, path, branch) { + path + (branch ? ("?ref=" + branch) : ""); } -var zipContents = function(filename, contents){ - var currDate = new Date(); +function base64toBlob(base64Data, contentType) { + contentType = contentType || ''; + var sliceSize = 1024; + var byteCharacters = atob(base64Data); + var bytesLength = byteCharacters.length; + var slicesCount = Math.ceil(bytesLength / sliceSize); + var byteArrays = new Array(slicesCount); + + for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { + var begin = sliceIndex * sliceSize; + var end = Math.min(begin + sliceSize, bytesLength); + + var bytes = new Array(end - begin); + for (var offset = begin, i = 0; offset < end; ++i, ++offset) { + bytes[i] = byteCharacters[offset].charCodeAt(0); + } + byteArrays[sliceIndex] = new Uint8Array(bytes); + } + return new Blob(byteArrays, { type: contentType }); +} + +function zipContents(filename, contents){ + var currDate = new Date(); var dateWithOffset = new Date(currDate.getTime() - currDate.getTimezoneOffset() * 60000); // replace the default date with dateWithOffset JSZip.defaults.date = dateWithOffset; var zip = new JSZip(); - contents.forEach(function(item){ - zip.file(item.path, item.content, { createFolders:true, base64:true }); + zip.file(item.path, item.content, {createFolders:true,base64:true}); }); return new Promise(function(res, rej){ zip.generateAsync({type:"blob"}) @@ -77,12 +115,12 @@ function callAjax(url, token){ xmlhttp.onreadystatechange = function(){ if (xmlhttp.readyState == 4){ if(xmlhttp.status == 200){ - resolve(xmlhttp.response); + resolve(xmlhttp); }else if(xmlhttp.status >= 400){ - reject(xmlhttp.response); + reject(xmlhttp); } } - }; + } xmlhttp.responseType = "json"; xmlhttp.open("GET", url, true); if ( token ) xmlhttp.setRequestHeader("Authorization", "token " + token); @@ -90,26 +128,40 @@ function callAjax(url, token){ }); } -var itemCollectSelector = ".repository-content .js-navigation-item"; +function hasRepoContainer(list) { + if ( list && list.length ) { + for (var i = 0, len = list.length; i < len; i++) { + var item = list[i]; + if (item.querySelector && item.querySelector(".repository-content")) { + return true; + } + } + } + return false; +} + +var itemCollectSelector = ".repository-content div.js-navigation-item"; var Pool = { _locked: false, _el: null, _dashBody: null, + _arrow: null, init: function(){ // create dom // Make the dom on right bottom var self = this; - var isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if(!self._el){ var wrap = document.createElement('div'), + arrow = document.createElement('div'), dash = document.createElement('div'), down = document.createElement('p'), tip = document.createElement('p'); wrap.className = "gitzip-collect-wrap" + (isDark ? " gitzip-dark" : ""); dash.className = "gitzip-collect-dash"; + arrow.className = "gitzip-collect-arrow"; down.className = "gitzip-collect-down"; tip.className = "gitzip-collect-tip"; @@ -122,6 +174,13 @@ var Pool = { var c = document.createElement("div"); c.className = "gitzip-header"; c.appendChild(document.createTextNode("Progress Dashboard")); + + var close = document.createElement("span"); + close.className = "gitzip-close"; + close.appendChild(document.createTextNode("\u2715")); + close.addEventListener('click', function(){ self.reset(); }); + + c.appendChild(close); return c; })() ); @@ -134,13 +193,19 @@ var Pool = { })() ); + // arrow + arrow.appendChild(down); + arrow.appendChild(tip); + + // wrap + wrap.appendChild(arrow); wrap.appendChild(dash); - wrap.appendChild(down); - wrap.appendChild(tip); + document.body.appendChild(wrap); self._el = wrap; self._dashBody = dash.querySelector(".gitzip-body"); + self._arrow = arrow; // hook events down.addEventListener('click', function(){ self.download(); }); @@ -149,245 +214,622 @@ var Pool = { self.reset(); }, - show: function(){ this._el && this._el.classList.add("gitzip-show"); }, - hide: function(){ this._el && this._el.classList.remove("gitzip-show"); }, + show: function(){ this._arrow && this._arrow.classList.add("gitzip-show"); }, + hide: function(){ this._arrow && this._arrow.classList.remove("gitzip-show"); }, reset: function(){ var self = this; - !!checkHaveAnyCheck()? self.show() : self.hide(); + checkHaveAnyCheck()? self.show() : self.hide(); self._el.classList.remove("gitzip-downloading"); + self._el.classList.remove("gitzip-fail"); while (self._dashBody.firstChild) { self._dashBody.removeChild(self._dashBody.firstChild); } self._locked = false; }, - download: function(){ + checkTokenAndScope: function(){ var self = this; - if(self._locked) return; - - self._locked = true; + var checkUrl = "https://api.github.com/rate_limit"; + var isPrivate = !!document.querySelector(".flex-auto .octicon-lock"); - self._el.classList.add("gitzip-downloading"); + return browser.runtime.sendMessage({action: "getKey"}) + .then(function(key){ + + if ( !key ) { + if ( isPrivate ) return Promise.reject("You should have token with `repo` scope."); + else { + self.log("Running in the rate limit.", "warn"); + return key; + } + } - var checkedItems = document.querySelectorAll(itemCollectSelector + " p.gitzip-show"); + self.log("Check token and scopes..."); + + return callAjax(checkUrl, key) + .then(function(xmlResponse){ + // return status 200 means token is valid + if ( isPrivate ) { + var strScopes = xmlResponse.getResponseHeader("X-OAuth-Scopes"); + var scopes = strScopes ? strScopes.split(",").map(function(str){ return str.trim(); }) : null; + if ( !scopes || scopes.indexOf("repo") == -1 ) { + return Promise.reject("Your token cannot access private repo."); + } + } + return key; + }); + }).catch(function(err){ + if ( typeof err == "string" ) { + self.log(err, "error"); + self.log("Please click GitZip extension icon to get private token.", "warn"); + return Promise.reject(); + } else return Promise.reject(err); + }); + }, + handleApiError: function(xmlResponse){ + var self = this; + if ( xmlResponse ) { + var status = xmlResponse.status; + var response = xmlResponse.response; + var message = (response && response.message) ? response.message : xmlResponse.statusText; + self.log(message, "error"); + if (message.indexOf("rate limit exceeded") >= 0){ + self.log("Please click GitZip extension icon to get token or input your token.", "warn"); + } + if ( status == 401 ) { + self.log("Your token is invalid, please re-login github and get token again.", "warn"); + } + } + }, + downloadPromiseProcess: function(resolvedUrl, infoAjaxItems){ + var self = this, + fileContents = [], + currentKey = ""; - self.log("Collect checked items..."); var treeAjaxItems = []; var blobAjaxCollection = []; - var fileContents = []; + + // start progress + self.checkTokenAndScope().then(function(key){ + currentKey = key || ""; + var infoUrl = getInfoUrl(resolvedUrl.author, resolvedUrl.project, resolvedUrl.path, resolvedUrl.branch); + return callAjax(infoUrl, currentKey).then(function(xmlResponse){ + var listRes = xmlResponse.response; + listRes + .filter(function(item){ + return infoAjaxItems.some(function(info){ + return (info.title === item.name || info.alias === item.name) && ( + (info.type == 'tree' && item.type == 'dir') || + (info.type == 'blob' && item.type == 'file') + ); + }); + }) + .forEach(function(item){ + if(item.type == "dir"){ + treeAjaxItems.push({ title: item.name, url: item.git_url }); + }else{ + blobAjaxCollection.push({ path: item.name, blobUrl: item.git_url }); + self.log(item.name + " url fetched.") + } + }); + }); + }).then(function(){ + var promises = treeAjaxItems.map(function(item){ + var fetchedUrl = item.url + "?recursive=1"; + return callAjax(fetchedUrl, currentKey).then(function(xmlResponse){ + var treeRes = xmlResponse.response; + treeRes.tree.forEach(function(blobItem){ + if(blobItem.type == "blob"){ + var path = item.title + "/" + blobItem.path; + blobAjaxCollection.push({ path: path, blobUrl: blobItem.url }); + self.log(path + " url fetched."); + } + }); + }); + }); + return Promise.all(promises); + }).then(function(){ + self.log("Collect blob contents..."); + var promises = blobAjaxCollection.map(function(item){ + var fetchedUrl = item.blobUrl; + return callAjax(fetchedUrl, currentKey).then(function(xmlResponse){ + var blobRes = xmlResponse.response; + fileContents.push({ path: item.path, content: blobRes.content }); + self.log(item.path + " content has collected."); + }); + }); + return Promise.all(promises); + }).then(function(){ + if ( treeAjaxItems.length == 0 && blobAjaxCollection.length == 1) { + self.log("Trigger download..."); + // to save as file + var singleItem = fileContents[0]; + return saveAs(base64toBlob(singleItem.content), singleItem.path); + } else { + self.log("Zip contents and trigger download..."); + return zipContents([resolvedUrl.project].concat(resolvedUrl.path.split('/')).join('-'), fileContents); + } + }).then(function(){ + self.reset(); + }).catch(function(err){ + self.handleApiError(err); + }); + }, + downloadItems: function(items){ + var self = this; + if(self._locked || !items.length) return; + + self._locked = true; + + self._el.classList.add("gitzip-downloading"); + var infoAjaxItems = []; var resolvedUrl = resolveUrl(window.location.href); - var currentKey = ""; - + self.log("Collect blob urls..."); - for(var idx = 0, len = checkedItems.length; idx < len; idx++){ - var item = checkedItems[idx], - href = item.getAttribute('gitzip-href'), + for(var idx = 0, len = items.length; idx < len; idx++){ + var item = isOnlyDoubleClick ? items[idx] : items[idx].closest("div.gitzip-check-wrap"), type = item.getAttribute('gitzip-type'), - title = item.getAttribute('gitzip-title'); + title = item.getAttribute('gitzip-title'), + href = item.getAttribute('gitzip-href'); - infoAjaxItems.push({ + var itemInfo = { type: type, title: title, href: href - }); + }, titleSplits; - // ga - // var looklink = item.closest("tr").querySelector("td.content a"); - // if(looklink){ - // var baseRepo = [resolvedUrl.author, resolvedUrl.project].join("/"); - // var githubUrl = looklink.getAttribute("href").substring(1); // ignore slash "/" from begin - // browser.runtime.sendMessage({ - // action: "gaTrack", - // baseRepo: baseRepo, - // githubUrl: githubUrl, - // userAction: "collected" - // }); - // } + if ( (titleSplits = title.split("/")).length > 1 ) itemInfo.alias = titleSplits[0]; + infoAjaxItems.push(itemInfo); } + + self.downloadPromiseProcess(resolvedUrl, infoAjaxItems); + }, + downloadSingle: function(selectedEl){ + this.downloadItems( selectedEl.querySelectorAll(isOnlyDoubleClick ? "p.gitzip-check-mark" : "div.gitzip-check-wrap") ); + }, + downloadAll: function(){ + this.downloadItems(document.querySelectorAll(itemCollectSelector + (isOnlyDoubleClick ? " p.gitzip-check-mark" : " div.gitzip-check-wrap") )); + }, + download: function(){ + this.downloadItems(document.querySelectorAll(itemCollectSelector + (isOnlyDoubleClick ? " p.gitzip-show" : " div.gitzip-check-wrap input:checked") )); + }, + downloadFile: function(resolvedUrl){ + var self = this; + if(self._locked) return; - // start progress - browser.runtime - .sendMessage({action: "getKey"}) - .then(function(key){ - currentKey = key || ""; - var infoUrl = getInfoUrl(resolvedUrl.author, resolvedUrl.project, resolvedUrl.path, resolvedUrl.branch); - return callAjax(infoUrl, currentKey).then(function(listRes){ - listRes - .filter(function(item){ - return infoAjaxItems.some(function(info){ - return info.title == item.name && ( - (info.type == 'tree' && item.type == 'dir') || - (info.type == 'blob' && item.type == 'file') - ); - }); - }) - .forEach(function(item){ - if(item.type == "dir"){ - treeAjaxItems.push({ title: item.name, url: item.git_url }); - }else{ - blobAjaxCollection.push({ path: item.name, blobUrl: item.git_url }); - self.log(item.name + " url fetched.") - } - }); - }); - }) - .then(function(){ - var promises = treeAjaxItems.map(function(item){ - var fetchedUrl = item.url + "?recursive=1"; - return callAjax(fetchedUrl, currentKey).then(function(treeRes){ - treeRes.tree.forEach(function(blobItem){ - if(blobItem.type == "blob"){ - var path = item.title + "/" + blobItem.path; - blobAjaxCollection.push({ path: path, blobUrl: blobItem.url }); - self.log(path + " url fetched."); - } - }); - }); - }); - return Promise.all(promises); - }) - .then(function(){ - self.log("Collect blob contents..."); - var promises = blobAjaxCollection.map(function(item){ - var fetchedUrl = item.blobUrl; - return callAjax(fetchedUrl, currentKey).then(function(blobRes){ - fileContents.push({ path: item.path, content: blobRes.content }); - self.log(item.path + " content has collected."); - }); - }); - return Promise.all(promises); - }) - .then(function(){ - self.log("Zip contents and trigger download..."); - return zipContents([resolvedUrl.project].concat(resolvedUrl.path.split('/')).join('-'), fileContents); - }) - .then(function(){ self.reset(); }) - .catch(function(err){ - console.log(err); - var message = err.message? err.message : err; - self.log(message); - if (message.indexOf("rate limit exceeded") >= 0){ - self.log("Please press GitZip extension icon to get token or input your token."); - } - if (message.indexOf("Bad credentials") >= 0){ - self.log("Your token has expired, please get token again."); - } - }); + self._locked = true; + + self._el.classList.add("gitzip-downloading"); + + var breadcrumb = document.querySelector(".repository-content .file-navigation .js-path-segment"), + rootAnchor = breadcrumb ? breadcrumb.querySelector("a") : null; + if ( rootAnchor && rootAnchor.href ) { + // for the cases like this: https://github.com/Microsoft/CNTK/blob/aayushg/autoencoder/Tools/build-and-test + // to find the branch in the case of branch has slash charactor. + var hrefSplits = rootAnchor.href.split("/tree/"); + if ( hrefSplits.length > 1 && resolvedUrl.branch != hrefSplits[1] ) { + var newBranch = hrefSplits[1]; + var inputSplits = resolvedUrl.inputUrl.split(newBranch); + var newPath = inputSplits[1].slice(1); + var newRoot = "https://github.com/" + resolvedUrl.author + "/" + resolvedUrl.project + "/tree/" + newBranch; + + resolvedUrl.branch = newBranch; + resolvedUrl.path = newPath; + resolvedUrl.rootUrl = newRoot; + } + } + + self.checkTokenAndScope().then(function(key){ + self.log("Collect blob content..."); + + currentKey = key || ""; + var params = []; + var fetchedUrl = "https://api.github.com/repos/" + resolvedUrl.author + "/" + resolvedUrl.project + "/contents/" + resolvedUrl.path; + + if ( resolvedUrl.branch ) params.push("ref=" + resolvedUrl.branch); + if ( params.length ) fetchedUrl += "?" + params.join('&'); + + return callAjax(fetchedUrl, currentKey); + }).then(function(xmlResponse){ + var treeRes = xmlResponse.response; + self.log(treeRes.name + " content has collected."); + self.log("Trigger download..."); + return saveAs(base64toBlob(treeRes.content), treeRes.name); + }).then(function(){ + self.reset(); + }).catch(function(err){ + self.handleApiError(err); + }); }, - log: function(message){ - this._dashBody.appendChild(document.createTextNode(message)); - this._dashBody.appendChild(document.createElement("br")); - this._dashBody.scrollTop = this._dashBody.scrollHeight - this._dashBody.clientHeight; + log: function(message, type){ + var self = this, + pNode = document.createElement("p"), + textNode = document.createTextNode(message); + + type && pNode.classList.add(type); + if (type == "error") self._el.classList.add("gitzip-fail"); + + pNode.appendChild(textNode); + + self._dashBody.appendChild(pNode); + self._dashBody.scrollTop = self._dashBody.scrollHeight - self._dashBody.clientHeight; } }; +function applyTheme() { + if (Pool._el) { + isDark && Pool._el.classList.add("gitzip-dark"); + !isDark && Pool._el.classList.remove("gitzip-dark"); + } + var markers = document.querySelectorAll("p.gitzip-check-mark"); + var markersLength = markers.length; + for(var i = 0; i < markersLength; i++){ + isDark && markers[i].classList.add("gitzip-dark"); + !isDark && markers[i].classList.remove("gitzip-dark"); + } +} + +browser.storage.local.get(defaultOptions, function(items){ + if (items) { + if (items.theme == "default") isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + else isDark = items.theme == "dark"; + applyTheme(); + + isOnlyDoubleClick = items.selectBehaviour == "double-click"; + isOnlySingleCheck = items.selectBehaviour == "single-check"; + isBoth = items.selectBehaviour == "both"; + + isStorageCallback = true; + var callbackEvent = new CustomEvent("storagecallback", {}); + window.dispatchEvent(callbackEvent); + } +}); + +browser.storage.onChanged.addListener(function(changes, area){ + if (area == "local") { + if (changes.theme) { + var newValue = changes.theme.newValue; + if (newValue == "default") isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + else isDark = newValue == "dark"; + applyTheme(); + } + + if (changes.selectBehaviour) { + var newValue = changes.selectBehaviour.newValue; + + isOnlyDoubleClick = newValue == "double-click"; + isOnlySingleCheck = newValue == "single-check"; + isBoth = newValue == "both"; + + // rebind + var items = document.querySelectorAll(itemCollectSelector); + var itemLen = items.length; + if(itemLen){ + for(var i = 0; i < itemLen; i++){ + var item = items[i]; + // reset + item._hasBind = false; + + // remove events + item.removeEventListener("dblclick", onItemDblClick); + item.removeEventListener("mouseenter", onItemEnter); + item.removeEventListener("mouseleave", onItemLeave); + + // remove custom markers + var toRemoveMark = item.querySelector("p.gitzip-check-mark"); + var toRemoveWrap = item.querySelector("div.gitzip-check-wrap"); + toRemoveMark && toRemoveMark.remove(); + toRemoveWrap && toRemoveWrap.remove(); + } + } + appendToIcons(true); + } + } +}); + function createMark(parent, height, title, type, href){ - if(parent && !parent.querySelector("p.gitzip-check-mark")){ - var checkp = document.createElement('p'); - - checkp.setAttribute("gitzip-title", title); - checkp.setAttribute("gitzip-type", type); - checkp.setAttribute("gitzip-href", href); - checkp.className = "gitzip-check-mark"; - checkp.appendChild(document.createTextNode("\u2713")); - checkp.style.cssText = "line-height:" + height + "px;"; - - parent.appendChild(checkp); + var target = parent.querySelector(isOnlyDoubleClick ? "p.gitzip-check-mark" : "div.gitzip-check-wrap"); + if (parent && !target) { + if (isOnlyDoubleClick) { + var checkp = document.createElement('p'); + + checkp.setAttribute("gitzip-title", title); + checkp.setAttribute("gitzip-type", type); + checkp.setAttribute("gitzip-href", href); + checkp.className = (isDark ? "gitzip-dark " : "") + "gitzip-check-mark"; + checkp.appendChild(document.createTextNode("\u2713")); + checkp.style.cssText = "line-height:" + height + "px;"; + + parent.appendChild(checkp); + + target = checkp; + } else { + var checkw = document.createElement('div'); + var cb = document.createElement("input"); + + cb.type = "checkbox"; + + checkw.setAttribute("gitzip-title", title); + checkw.setAttribute("gitzip-type", type); + checkw.setAttribute("gitzip-href", href); + + checkw.className = "gitzip-check-wrap"; + + checkw.appendChild(cb); + parent.appendChild(checkw); + + target = checkw; + } } + return target; } function checkHaveAnyCheck(){ - var checkItems = document.querySelectorAll(itemCollectSelector + " p.gitzip-show"); - return checkItems.length? checkItems : false; + var checkItems = document.querySelectorAll(itemCollectSelector + (isOnlyDoubleClick ? " p.gitzip-show" : " div.gitzip-check-wrap input:checked") ); + return checkItems.length > 0; } function onItemDblClick(e){ - var markTarget = e.target.closest(".js-navigation-item").querySelector('p.gitzip-check-mark'); - if(markTarget) markTarget.classList.toggle("gitzip-show"); - !!checkHaveAnyCheck()? Pool.show() : Pool.hide(); + if (isBoth) { + var markTarget = e.target.closest(".js-navigation-item").querySelector('div.gitzip-check-wrap'); + if(markTarget) { + var cb = markTarget.querySelector('input'); + cb.click(); + } + } else if (isOnlyDoubleClick) { + var markTarget = e.target.closest(".js-navigation-item").querySelector('p.gitzip-check-mark'); + if(markTarget) markTarget.classList.toggle("gitzip-show"); + checkHaveAnyCheck()? Pool.show() : Pool.hide(); + applyItemsContext(); + } } -function hookItemEvents(){ - - // show icon in every github page. - showGitzipIcon(); - - function appendToIcons(){ - var items = document.querySelectorAll(itemCollectSelector); - var itemLen = items.length; - if(itemLen){ - for(var i = 0; i < itemLen; i++){ - var item = items[i], - link = item.querySelector("a[href]"), - blob = item.querySelector(".octicon-file-text, .octicon-file"), - tree = item.querySelector(".octicon-file-directory"); +function onItemEnter(e) { + var markTarget = e.target.closest(".js-navigation-item").querySelector('div.gitzip-check-wrap'); + if (markTarget && !markTarget.style.display) { + markTarget.style.display = "flex"; + } +} + +function onItemLeave(e) { + var markTarget = e.target.closest(".js-navigation-item").querySelector('div.gitzip-check-wrap'); + if (markTarget && !markTarget.querySelector('input:checked')) { + markTarget.style.display = ""; + } +} + +var cacheSelectEl = null; +var currentSelectEl = null; +function generateEnterItemHandler(title, type){ + return function(){ + applySelectedContext(title, type); + cacheSelectEl = this; + } +} + +function leaveItemHandler() { + applySelectedContext(); + cacheSelectEl = null; +} + +function applyItemsContext() { + browser.runtime.sendMessage({ + action: "updateContextNested", + target: "items", + enabled: checkHaveAnyCheck() + }); +} + +function applySelectedContext(name, type) { + var enabled = !!name && !!type; + browser.runtime.sendMessage({ + action: "updateContextNested", + urlName: name, + urlType: type, + enabled: enabled, + target: "selected" + }); +} + +function applyCurrentContext(name, type) { + browser.runtime.sendMessage({ + action: "updateContextNested", + urlName: name, + urlType: type, + root: !name && !type, + target: "current" + }); +} + +function restoreContextStatus(){ + var resolvedUrl = resolveUrl(window.location.href); + var baseRepo = [resolvedUrl.author, resolvedUrl.project].join("/"); + var fileNavigation = document.querySelector(".repository-content .file-navigation"); + var singleFileNavigation = document.querySelector(".repository-content .breadcrumb .js-repo-root"); + var downloadBtn = fileNavigation ? fileNavigation.querySelector("div[data-target='get-repo.modal'] a[href^='/" + baseRepo + "/']") : null; + var pathText = resolvedUrl.path.split('/').pop(); + var urlType = resolvedUrl.type; + var breadcrumb; + + if ( fileNavigation && (breadcrumb = fileNavigation.querySelector(".js-repo-root")) ) { + // in tree view + applyCurrentContext(pathText, urlType); + } else if ( singleFileNavigation ) { + // in file view + applyCurrentContext(pathText, urlType); + applySelectedContext(); + } else if (downloadBtn) { + // in root + applyCurrentContext(); + } + + // the checked items + applyItemsContext(); +} + +// Check is in available view +function isAvailableView(){ + return !!document.querySelector("head meta[value=repo_source]") && resolveUrl(window.location.href) !== false; +} + +function isItemsBind() { + var items = document.querySelectorAll(itemCollectSelector); + var itemLen = items.length; + return itemLen ? !!items[itemLen-1]._hasBind : false; +} + +function appendToIcons(isRebind){ + var items = document.querySelectorAll(itemCollectSelector); + var itemLen = items.length; + if(itemLen){ + for(var i = 0; i < itemLen; i++){ + var item = items[i], + link = item.querySelector("a[href]"), + blob = item.querySelector(".octicon-file-text, .octicon-file"), + tree = item.querySelector(".octicon-file-directory, .octicon-file-directory-fill"); + + if(!item._hasBind && link && (tree || blob)){ + var title = link.textContent, + type = tree? "tree" : "blob"; + + if (isBoth || isOnlySingleCheck) { + // reset status if not checked + onItemLeave({ target: item }); + } + + var markTarget = createMark(item, item.offsetHeight, title, type, link.href); + if (isBoth || isOnlySingleCheck) { + markTarget.querySelector("input").addEventListener('change', function(){ + checkHaveAnyCheck()? Pool.show() : Pool.hide(); + applyItemsContext(); + }); + } + + if (isBoth || isOnlyDoubleClick) { + item.addEventListener("dblclick", onItemDblClick); + } - if(!item._hasBind && link && (tree || blob)){ - createMark( - item, - item.offsetHeight, - link.textContent, - tree? "tree" : "blob", - link.href - ); - item.addEventListener("dblclick", onItemDblClick); - item._hasBind = true; + if (isRebind !== true) { + item.addEventListener("mouseenter", generateEnterItemHandler(title, type, link.href) ); + item.addEventListener("mouseleave", leaveItemHandler); } + + if (isBoth || isOnlySingleCheck) { + item.addEventListener("mouseenter", onItemEnter); + item.addEventListener("mouseleave", onItemLeave); + } + + item._hasBind = true; } } } +} - var lazyCaseObserver = null; - var repoContent = document.querySelector(".repository-content"); - var lazyElement = repoContent ? repoContent.querySelector(".js-navigation-container") : null; - - if(lazyElement){ - // lazy case - lazyCaseObserver = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - var addNodes = mutation.addedNodes; - addNodes && addNodes.length && addNodes.forEach(function(el){ - if(el.classList && (el.classList.contains("js-details-container") || el.classList.contains("js-navigation-container"))){ - appendToIcons(); - lazyCaseObserver.disconnect(); - lazyCaseObserver = null; - } - }); - }); - }); - lazyCaseObserver.observe(repoContent, { childList: true, subtree: true } ); - } +function isAnyItemExist() { + return !!document.querySelector(itemCollectSelector); +} + +function hookItemEvents(){ + + var theInterval = null; + + function waitStorageHandler() { + appendToIcons(); + Pool._el && Pool.reset(); + currentSelectEl = cacheSelectEl = null; + + if (!theInterval) { + theInterval = setInterval(function(){ + if (!isAnyItemExist() || isItemsBind()) { + clearInterval(theInterval); + theInterval = null; + } else { + appendToIcons(); + } + }, 100); + } + } - if (document.querySelector(itemCollectSelector)) { - appendToIcons(); + if (isAnyItemExist()) { + if (isStorageCallback) waitStorageHandler(); + else window.addEventListener("storagecallback", waitStorageHandler); } + window.addEventListener('popstate', (ev) => { + if (isAnyItemExist()) { + waitStorageHandler(); + } + }); + + window.addEventListener('contextmenu', (ev) => { + currentSelectEl = cacheSelectEl; + }) + + function onRequestsObserved( batch ) { + var entries = batch.getEntries(); + if (entries.some(resource => repoCommitExp.test(resource.name)) && isAnyItemExist()) { + if (isStorageCallback) waitStorageHandler(); + else window.addEventListener("storagecallback", waitStorageHandler); + } + } + + var requestObserver = new PerformanceObserver( onRequestsObserved ); + requestObserver.observe({ type: 'resource' }); + Pool.init(); } -// pjax detection -function hookMutationObserver(){ - // select the target node - var target = document.querySelector("*[data-pjax-container]"); +function hookChromeEvents(){ - // create an observer instance - var observer = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - var addNodes = mutation.addedNodes; - if(addNodes && addNodes.length) hookItemEvents(); - }); + browser.runtime.onMessage.addListener(function(request, sender, sendResponse) { + switch (request.action){ + case "getCurrentPath": + sendResponse(window.location.href); + case "github-tab-active": + // from the background event + // means tab active changed. + // if it is in github.com now + browser.runtime.sendMessage({action: "showIcon"}); + if ( isAvailableView() ) { + browser.runtime.sendMessage({action: "createContextNested"}); + restoreContextStatus(); + } else { + browser.runtime.sendMessage({action: "removeContext"}); + } + return true; + case "gitzip-nested-items-clicked": + Pool.download(); + return true; + case "gitzip-nested-selected-clicked": + Pool.downloadSingle(currentSelectEl); + return true; + case "gitzip-nested-current-clicked": + var resolvedUrl = resolveUrl(window.location.href); + var baseRepo = [resolvedUrl.author, resolvedUrl.project].join("/"); + + var fileNavigation = document.querySelector(".repository-content .file-navigation"); + var singleFileNavigation = document.querySelector(".repository-content .breadcrumb .js-repo-root"); + + var breadcrumb, + downloadBtn = fileNavigation ? fileNavigation.querySelector("div[data-target='get-repo.modal'] a[href^='/" + baseRepo + "/']") : null; + + if ( fileNavigation && (breadcrumb = fileNavigation.querySelector(".js-repo-root")) ) { + // in tree view + Pool.downloadAll(); + } else if ( singleFileNavigation ) { + // in file view + Pool.downloadFile(resolvedUrl); + } else if ( downloadBtn ) { + // in root + downloadBtn.click(); + } else { + alert("Unknown Operation"); + } + return true; + } }); - - // pass in the target node, as well as the observer options - target && observer.observe(target, { childList: true } ); - - // later, you can stop observing - // observer.disconnect(); } -// Property run_at is "document_end" as default in Content Script -// refers: https://developer.chrome.com/extensions/content_scripts -hookMutationObserver(); hookItemEvents(); +hookChromeEvents(); diff --git a/js/popup.js b/js/popup.js index 38ccf3c..6d39ea8 100644 --- a/js/popup.js +++ b/js/popup.js @@ -1,3 +1,33 @@ + +let isDark = true; + +const defaultOptions = { + selectBehaviour: 'both', + theme: 'default' +}; + +function applyTheme() { + isDark && document.body.classList.add("dark-theme"); + !isDark && document.body.classList.remove("dark-theme"); +} + +browser.storage.local.get(defaultOptions, function(items){ + if (items) { + if (items.theme == "default") isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + else isDark = items.theme == "dark"; + applyTheme(); + } +}); + +browser.storage.onChanged.addListener(function(changes, area){ + if (area == "local" && changes.theme) { + var newValue = changes.theme.newValue; + if (newValue == "default") isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + else isDark = newValue == "dark"; + applyTheme(); + } +}); + // The DOMContentLoaded means the popup.html page has load. (trigger this event after click the ext icon) document.addEventListener('DOMContentLoaded', function() { // alert("has loaded"); @@ -35,4 +65,5 @@ document.addEventListener('DOMContentLoaded', function() { } }); + applyTheme(); }, false); diff --git a/manifest.json b/manifest.json old mode 100644 new mode 100755 index 8ec3b26..0edb163 --- a/manifest.json +++ b/manifest.json @@ -3,13 +3,17 @@ "name": "GitZip", "description": "It can make the sub-directories and files of github repository as zip and download it", - "version": "0.3.2", + "version": "1.0.0", "page_action": { - "default_icon": "images/icon-empty.png", + "default_icon": "images/icon-128px.png", "default_popup": "popup.html" }, + "options_ui": { + "page": "options.html" + }, + "background": { "page": "background.html" }, @@ -17,6 +21,7 @@ "permissions": [ "storage", "activeTab", + "contextMenus", "theme", "*://github.com/*", "https://api.github.com/repos/*" diff --git a/options.html b/options.html new file mode 100644 index 0000000..ee2360a --- /dev/null +++ b/options.html @@ -0,0 +1 @@ +Gitzip Options
\ No newline at end of file diff --git a/static/js/main.d164c8cf.js b/static/js/main.d164c8cf.js new file mode 100644 index 0000000..b76814c --- /dev/null +++ b/static/js/main.d164c8cf.js @@ -0,0 +1,3 @@ +/*! For license information please see main.d164c8cf.js.LICENSE.txt */ +!function(){"use strict";var e={110:function(e,t,n){var r=n(441),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},a={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function u(e){return r.isMemo(e)?i:l[e.$$typeof]||o}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var s=Object.defineProperty,c=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var o=p(n);o&&o!==h&&e(t,o,r)}var i=c(n);f&&(i=i.concat(f(n)));for(var l=u(t),m=u(n),v=0;v