Greasy Fork 支持简体中文。

Github Stats

Display download stats about the last release of Github projects.

  1. // ==UserScript==
  2. // @name Github Stats
  3. // @namespace stratehm.github
  4. // @include https://github.com/*/*
  5. // @version 7
  6. // @grant GM_xmlhttpRequest
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @grant GM_deleteValue
  10. // @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
  11. // @require https://greasyfork.org/scripts/2199-waitforkeyelements/code/waitForKeyElements.js
  12. // @description Display download stats about the last release of Github projects.
  13. // ==/UserScript==
  14.  
  15. var lastReleaseItemList;
  16. var cachedResponse;
  17.  
  18. this.$ = this.jQuery = jQuery.noConflict(true);
  19.  
  20. waitForKeyElements('.repohead-details-container', function () {
  21. init();
  22. });
  23.  
  24. function init() {
  25. $('#ulLastReleaseItems').remove();
  26. lastReleaseItemList = $('<ul id="ulLastReleaseItems"/>').attr({
  27. style: 'font-size: 11px; line-height: 10px; white-space: nowrap;'
  28. }).append('<b>Last release: </b>');
  29. $('div.repohead-details-container').find('h1').append(lastReleaseItemList);
  30. var userProject = getCurrentUserProjectUrlPart();
  31. if(userProject) {
  32. getDownloadCount(userProject);
  33. }
  34. if(window.location.pathname.indexOf("/settings/tokens") >= 0) {
  35. $('.column.three-fourths').append('<div class="boxed-group access-token-group" id="GM_Form"><h3>Set your Gihtub login credentials for the GreaseMonkey userscript</h3><div class="boxed-group-inner"><p>Your login credentials will be used by the userscript to show the number of downloads for repositories.</p><ul class="boxed-group-list"><li style="line-height:32px;"><p>Username: <input type="text" id="clientId" style="float:right;width:480px;" /></p><p style="line-height:32px;">Password: <input type="password" id="clientSecret" style="float:right;width:480px;"/></p></li><li><button id="GM_submit" type="submit" type="button" class="btn btn-primary">Save</button>&nbsp;&nbsp;<button id="GM_reset" type="submit" type="button" class="btn btn-danger">Clear</button></li></ul><p class="help"><i class="octicon octicon-question"></i>Without your login credentials, you are rate limited to <a href="https://developer.github.com/v3/#rate-limiting">60 api calls per hour</a>.</p></div></div>');
  36. $('#clientId').val(GM_getValue('clientId',''));
  37. $('#clientSecret').val(GM_getValue('clientSecret',''));
  38. $('#GM_submit').click(function() {
  39. GM_setValue("clientId",$('#clientId').val());
  40. GM_setValue("clientSecret",$('#clientSecret').val());
  41. console.log({clientId:GM_getValue("clientId"),clientSecret:GM_getValue("clientSecret")});
  42. $('#GM_submit').fadeTo(1000,0.01).fadeTo(1000,1);
  43. gotoSourceUrl();
  44. });
  45. $('#GM_done').fadeIn(1000).fadeOut(1000);
  46. $('#GM_reset').click(function(){
  47. $('#clientId').val('');
  48. $('#clientSecret').val('');
  49. GM_deleteValue("clientId");
  50. GM_deleteValue("clientSecret");
  51. $('#GM_reset').fadeTo(1000,0.01).fadeTo(1000,1);
  52. gotoSourceUrl();
  53. });
  54. } else {
  55. saveSourceUrl();
  56. }
  57. }
  58.  
  59. function getCurrentUserProjectUrlPart() {
  60. var splittedPath = window.location.pathname.split('/');
  61. if(splittedPath.length >= 3) {
  62. return splittedPath[1] + '/' + splittedPath[2];
  63. }
  64. }
  65.  
  66. function getDownloadCount(userProjectUserPart) {
  67. if(cachedResponse) {
  68. // Use the cached response if it exists.
  69. parseDownloadStatsResponse(cachedResponse);
  70. } else {
  71. var url = "https://api.github.com/repos/" + userProjectUserPart + "/releases";
  72. var headers = {
  73. "Cache-Control": "no-cache"
  74. }
  75. if(isTokenSet()) {
  76. headers.Authorization = "Basic " + btoa(GM_getValue("clientId")+":"+GM_getValue("clientSecret"));
  77. }
  78. GM_xmlhttpRequest({
  79. method: "GET",
  80. headers: headers,
  81. url: url,
  82. onload: onDownloadStatsResponse
  83. });
  84. }
  85. }
  86.  
  87. function onDownloadStatsResponse(response) {
  88. // Cache the response.
  89. cachedResponse = response;
  90. parseDownloadStatsResponse(response);
  91. }
  92.  
  93. function parseDownloadStatsResponse(response) {
  94. var status = response.status;
  95. var data = $.parseJSON(response.responseText);
  96. // Check if login credentials are accepted
  97. if(status == 401) {
  98. onUnauthorized();
  99. } else if(data.message && data.message.indexOf("API rate limit exceeded") >-1) {
  100. // Credentials are requested
  101. accessTokenNeeded();
  102. } else {
  103. // Parsing of the response only if some data are present.
  104. if(data && data.length > 0) {
  105. parseLastReleaseDownloadCount(data);
  106. parseTotalDownloadCount(data);
  107. } else {
  108. lastReleaseItemList.append("No release<br>");
  109. }
  110. if(isTokenSet()) {
  111. lastReleaseItemList.append("Change/Clear your <a href='https://github.com/settings/tokens'>Gihtub login credentials</a>");
  112. }
  113. }
  114. }
  115.  
  116. function parseLastReleaseDownloadCount(data) {
  117. var releaseName = data[0].name;
  118. var releaseDate = data[0].published_at;
  119. var htmlUrl = data[0].html_url;
  120. lastReleaseItemList.append($('<a/>').attr({
  121. href: htmlUrl,
  122. title: formatDate(releaseDate)
  123. }).append(releaseName));
  124. if(data[0].assets && data[0].assets.length > 0) {
  125. for(var i = 0 ; i < data[0].assets.length ; i++) {
  126. var assetName = data[0].assets[i].name;
  127. var assetDlCount = data[0].assets[i].download_count;
  128. var assetUrl = data[0].assets[i].browser_download_url;
  129. appendAssetDlItem(assetName, assetDlCount, assetUrl);
  130. }
  131. } else {
  132. lastReleaseItemList.append("<br>No binaries in the last release<br>");
  133. }
  134. }
  135.  
  136. function parseTotalDownloadCount(data) {
  137. var totalDownloadCount = 0;
  138. for(var i = 0 ; i < data.length ; i++) {
  139. if(data[i].assets && data[i].assets.length > 0) {
  140. for(var j = 0 ; j < data[i].assets.length ; j++) {
  141. totalDownloadCount += data[i].assets[j].download_count;
  142. }
  143. }
  144. }
  145. lastReleaseItemList.append("All releases download count: " + totalDownloadCount + "<br>");
  146. }
  147.  
  148. function appendAssetDlItem(assetName, assetDlCount, assetUrl) {
  149. lastReleaseItemList.append($('<li/>').attr({
  150. style: "margin-left: 20px;"
  151. }).append("<b>Name:</b> <a href='" + assetUrl + "'>" + assetName + '</a>, <b>Dl Count:</b> ' + assetDlCount));
  152. }
  153.  
  154.  
  155. function accessTokenNeeded() {
  156. lastReleaseItemList.append($('<li/>').attr({
  157. style: "margin-left: 20px;"
  158. }).append("Your api limit has been hit. Please add a <a href='https://github.com/settings/tokens'>Gihtub login credentials</a>"));
  159. }
  160.  
  161. function onUnauthorized() {
  162. lastReleaseItemList.append($('<li/>').attr({
  163. style: "margin-left: 20px;"
  164. }).append("Bad credentials. Please check your <a href='https://github.com/settings/tokens'>Gihtub login credentials</a>"));
  165. }
  166.  
  167. function isTokenSet() {
  168. return GM_getValue("clientId","") && GM_getValue("clientSecret","");
  169. }
  170.  
  171. function saveSourceUrl() {
  172. GM_setValue("sourceUrl", window.location.href);
  173. }
  174. function gotoSourceUrl() {
  175. var sourceUrl = GM_getValue("sourceUrl", "");
  176. console.log("Restore location: " + sourceUrl);
  177. if(sourceUrl) {
  178. window.location.href = sourceUrl;
  179. }
  180. }
  181.  
  182. function formatDate(dateToFormat) {
  183. var dateSeconds = Date.parse(dateToFormat);
  184. // build a date object with the (timezone-agnostic) timepoint
  185. var date = new Date(dateSeconds);
  186. // format the date according to locale's rules
  187. return date.toLocaleString();
  188. }