github commits { display: calendar; }

show github commits as calendar

  1. // ==UserScript==
  2. // @name github commits { display: calendar; }
  3. // @namespace leizingyiu.net
  4. // @include /^https:\/\/github.com\/[^/]+\/[^/]+\/(commits|commit)\/.*/
  5. // @grant GM_xmlhttpRequest
  6. // @run-at document-idle
  7. // @version 2024.06.12.17:44
  8. // @author leizingyiu
  9. // @license GNU GPLv3
  10. // @description show github commits as calendar
  11. // ==/UserScript==
  12.  
  13. /*
  14. history : 2024.06.04.01.26.04 : init
  15. 2024.06.12.17:44: add zh support ; fixed margin style ;
  16. */
  17. const is_zh = detectBrowserLanguage().indexOf('zh') != -1;
  18.  
  19. function detectBrowserLanguage() {
  20. var language;
  21.  
  22. if (navigator.languages && navigator.languages.length) { // Chrome, Firefox, IE11+
  23. language = navigator.languages[0];
  24. } else if (navigator.language) { // Modern browsers
  25. language = navigator.language;
  26. } else if (navigator.userLanguage) { // IE11-
  27. language = navigator.userLanguage;
  28. }
  29.  
  30. return language;
  31. }
  32.  
  33.  
  34. function getDayOfWeek(dateString) {
  35. const days = [0, 1, 2, 3, 4, 5, 6];
  36. const date = new Date(dateString);
  37. const dayIndex = date.getDay();
  38. return (days[dayIndex] + 6) % 7;
  39. }
  40.  
  41.  
  42. function addCSS(url, id, removeReg) {
  43. 'use strict';
  44.  
  45. if (document.getElementById(id) !== null) { return; }
  46. GM_xmlhttpRequest({
  47. method: 'GET',
  48. url: url,
  49. onload: function (response) {
  50. if (response.status === 200) {
  51. var style = document.createElement('style');
  52. style.id = id;
  53. style.textContent = response.responseText.replace(removeReg, '');
  54. document.head.appendChild(style);
  55. } else {
  56. console.error('Failed to load CSS file:', response.statusText);
  57. }
  58. },
  59. onerror: function (error) {
  60. console.error('Error loading CSS file:', error);
  61. }
  62. });
  63. }
  64.  
  65. function yiu_github_commits_calendar() {
  66.  
  67.  
  68. console.log('yiu-github-calendar start');
  69.  
  70. addCSS('https://userstyles.world/api/style/16743.user.css', 'yiu-github-commits-calendar-style', /(@[^{\n]*\{\n)|(\}(?![\n\r]))/g);
  71.  
  72. let vIdx = 0, phIdx = 7, nowHIdx = 0, startHIdx = 7;
  73. [...document.querySelectorAll(".mb-3 .Timeline-Item")].map((item, idx, arr) => {
  74. const days = is_zh ?
  75. ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] :
  76. ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
  77.  
  78. let date = item.querySelector("h3").innerText.replace("Commits on ", "");
  79.  
  80. let hIdx = getDayOfWeek(date); // 横向序号
  81.  
  82. if (hIdx >= phIdx) { // 如果当前序号大于上一个 , 说明这里换行
  83.  
  84. vIdx += 1;
  85. nowHIdx = 0;
  86. item.classList.add("clearFloat");
  87.  
  88. startHIdx = 7;
  89. }
  90.  
  91. startHIdx = hIdx < startHIdx ? hIdx : startHIdx;
  92.  
  93.  
  94.  
  95.  
  96. item.querySelector("h3").innerText =
  97. item.querySelector("h3").innerText.replace("Commits on ", "");
  98. item.setAttribute('yiu-github-calendar-day', `${days[hIdx]}`);
  99. item.querySelector("h3").setAttribute('yiu-github-calendar-day', `${days[hIdx]}`);
  100.  
  101.  
  102. Object.entries({
  103. "--yiu-github-calendar-offset": hIdx - nowHIdx,
  104. "--yiu-github-calendar-day": hIdx
  105. }).map(([k, v], _idx, _arr) => {
  106. item.style.setProperty(k, v);
  107. item.nextElementSibling.style.setProperty(k, v);
  108. });
  109.  
  110. item.setAttribute("yiu-github-calendar-row", vIdx);
  111.  
  112. nowHIdx += 1;
  113.  
  114. phIdx = hIdx;
  115.  
  116.  
  117. if (idx == arr.length - 1) {
  118. [...document.querySelectorAll(`[yiu-github-calendar-row="${vIdx}"]`)].map(
  119. (i) => {
  120. i.style.setProperty("--yiu-github-calendar-move", nowHIdx);
  121. i.nextElementSibling.style.setProperty("--yiu-github-calendar-move", nowHIdx);
  122. },
  123. );
  124. }
  125.  
  126. });
  127. console.log('yiu-github-calendar end ');
  128. }
  129.  
  130.  
  131.  
  132.  
  133.  
  134. const main = () => {
  135. yiu_github_commits_calendar();
  136. };
  137.  
  138. (function () {
  139. /* from https://greasyfork.org/zh-CN/scripts/434592 */
  140. 'use strict';
  141. /**
  142. * 在浏览器窗口大小改变时自动重新定位设置菜单
  143. */
  144. window.onresize = function () {
  145. // 监听窗口大小改变
  146. main();
  147. }
  148. let oldPushState = history.pushState;
  149. history.pushState = function pushState() {
  150. let ret = oldPushState.apply(this, arguments);
  151. window.dispatchEvent(new Event('pushstate'));
  152. window.dispatchEvent(new Event('locationchange'));
  153. return ret;
  154. };
  155.  
  156. let oldReplaceState = history.replaceState;
  157. history.replaceState = function replaceState() {
  158. let ret = oldReplaceState.apply(this, arguments);
  159. window.dispatchEvent(new Event('replacestate'));
  160. window.dispatchEvent(new Event('locationchange'));
  161. return ret;
  162. };
  163.  
  164. window.addEventListener('popstate', () => {
  165. window.dispatchEvent(new Event('locationchange'));
  166. });
  167.  
  168. document.addEventListener('pjax:success', function () {
  169. // 由于 GitHub 使用 pjax 而不是页面跳转的方式在仓库内导航,因此将 main 函数绑定到 pjax 监听器上
  170. window.dispatchEvent(new Event('locationchange'));
  171. });
  172. window.addEventListener('locationchange', function () {
  173. console.log('locationchange!');
  174. main();
  175. });
  176. main();
  177. })();