Github News Feed Filter

Add filters for Github homepage news feed items

当前为 2014-04-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Github News Feed Filter
  3. // @namespace https://github.com/jerone/UserScripts
  4. // @description Add filters for Github homepage news feed items
  5. // @author jerone
  6. // @homepage https://github.com/jerone/UserScripts/tree/master/Github_News_Feed_Filter
  7. // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_News_Feed_Filter
  8. // @include https://github.com/
  9. // @include https://github.com/orgs/*/dashboard
  10. // @version 4.6
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15.  
  16. var filters = [
  17. { text: "All News Feed", icon: "octicon-radio-tower", classNames: ["*"] },
  18. {
  19. text: "Issues", icon: "octicon-issue-opened", classNames: ["issues_comment", "issues_opened", "issues_closed", "issues_reopened"], subFilters: [
  20. { text: "Comments", icon: "octicon-comment-discussion", classNames: ["issues_comment"] },
  21. { text: "Opened", icon: "octicon-issue-opened", classNames: ["issues_opened"] },
  22. { text: "Closed", icon: "octicon-issue-closed", classNames: ["issues_closed"] },
  23. { text: "Reopened", icon: "octicon-issue-reopened", classNames: ["issues_reopened"] }
  24. ]
  25. },
  26. { text: "Commits", icon: "octicon-git-commit", classNames: ["push", "commit_comment"] },
  27. { text: "Pull Requests", icon: "octicon-git-pull-request", classNames: ["pull_request", "pull_request_comment"] },
  28. { text: "Repo", icon: "octicon-repo", classNames: ["create", "delete", "public", "release", "fork"] },
  29. {
  30. text: "User", icon: "octicon-person", classNames: ["watch_started", "member_add", "team_add"], subFilters: [
  31. { text: "Starred", icon: "octicon-star", classNames: ["watch_started"] },
  32. { text: "Member added", icon: "octicon-person-add", classNames: ["member_add", "team_add"] }
  33. ]
  34. },
  35. { text: "Wiki", icon: "octicon-book", classNames: ["gollum"] },
  36. {
  37. text: "Gist", icon: "octicon-gist", classNames: ["gist_created", "gist_updated"], subFilters: [
  38. { text: "Created", icon: "octicon-gist-new", classNames: ["gist_created"] },
  39. { text: "Updated", icon: "octicon-gist", classNames: ["gist_updated"] }
  40. ]
  41. }
  42. // Possible other classes: follow
  43. ];
  44.  
  45. function proxy(fn) {
  46. return function() {
  47. var that = this;
  48. return function(e) {
  49. var args = that.slice(0); // clone;
  50. args.unshift(e); // prepend event;
  51. fn.apply(this, args);
  52. };
  53. }.call([].slice.call(arguments, 1));
  54. }
  55.  
  56. function addFilterMenuItem(filter, parent, container, sidebar) {
  57. var a = document.createElement("a");
  58. a.classList.add("filter-item");
  59. a.setAttribute("href", "/");
  60. a.setAttribute("title", filter.classNames.join(" & "));
  61. if (filter.classNames[0] === "*") {
  62. a.classList.add("selected");
  63. a.style.fontWeight = "bold";
  64. }
  65.  
  66. var s = document.createElement("span");
  67. s.classList.add("octicon", filter.icon);
  68. s.style.marginRight = "10px";
  69. s.style.cssFloat = "left";
  70. s.style.minWidth = "16px";
  71. a.appendChild(s);
  72.  
  73. var c = document.createElement("span");
  74. c.classList.add("count");
  75. c.appendChild(document.createTextNode("0"));
  76. a.appendChild(c);
  77.  
  78. a.appendChild(document.createTextNode(filter.text));
  79.  
  80. a.addEventListener("click", proxy(function(e, classNames) {
  81. e.preventDefault();
  82.  
  83. var any = false,
  84. all = classNames[0] === "*",
  85. some = function(alert) { return classNames.some(function(cl) { return alert.classList.contains(cl); }); };
  86. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  87. alert.style.display = (all || some(alert)) && (any = true) ? "block" : "none";
  88. });
  89. var none = container.querySelector(".no-alerts");
  90. if (any && none) {
  91. none.parentNode.removeChild(none);
  92. } else if (!any && !none) {
  93. none = document.createElement("div");
  94. none.classList.add("no-alerts");
  95. none.style.padding = "0 0 1em 45px";
  96. none.style.fontStyle = "italic";
  97. none.appendChild(document.createTextNode("No feed items for this filter. Press the button below to load more items..."));
  98. container.insertBefore(none, container.firstChild);
  99. }
  100.  
  101. Array.forEach(sidebar.querySelectorAll(".filter-list.small"), function(subUl) { subUl.style.display = "none"; });
  102. var subUl = this.parentNode.querySelector("ul");
  103. if (!subUl && this.parentNode.parentNode.classList.contains("filter-list") && this.parentNode.parentNode.classList.contains("small")) {
  104. subUl = this.parentNode.parentNode;
  105. }
  106. subUl && (subUl.style.display = "block");
  107.  
  108. Array.forEach(sidebar.querySelectorAll(".selected"), function(m) { m.classList.remove("selected"); });
  109. this.classList.add("selected");
  110. }, filter.classNames));
  111.  
  112. var li = document.createElement("li");
  113. li.appendChild(a);
  114. li.filterClassNames = filter.classNames;
  115.  
  116. parent.appendChild(li);
  117.  
  118. return li;
  119. }
  120.  
  121. function addFilters() {
  122. var container = document.querySelector(".news");
  123. if (!container) return;
  124.  
  125. var sidebar = document.querySelector(".dashboard-sidebar");
  126.  
  127. var rule = document.createElement("div");
  128. rule.classList.add("rule");
  129. sidebar.insertBefore(rule, sidebar.firstChild);
  130.  
  131. var ul = document.createElement("ul");
  132. ul.classList.add("filter-list");
  133. sidebar.insertBefore(ul, sidebar.firstChild);
  134.  
  135. filters.forEach(function(filter) {
  136. var li = addFilterMenuItem(filter, ul, container, sidebar);
  137.  
  138. if (filter.subFilters) {
  139. var subUl = document.createElement("ul");
  140. subUl.classList.add("filter-list", "small");
  141. subUl.style.marginLeft = "10px";
  142. subUl.style.display = "none";
  143. li.appendChild(subUl);
  144.  
  145. filter.subFilters.forEach(function(subFilter) {
  146. addFilterMenuItem(subFilter, subUl, container, sidebar);
  147. });
  148. }
  149. });
  150.  
  151. // update on clicking "More"-button;
  152. unsafeWindow.$.pageUpdate(function() {
  153. window.setTimeout(function() {
  154. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  155. if (alert.getElementsByClassName("octicon-git-pull-request").length > 0) {
  156. alert.classList.remove("issues_opened", "issues_closed");
  157. alert.classList.add("pull_request");
  158. } else if (alert.classList.contains("issues_comment") && alert.querySelectorAll(".title a")[1].getAttribute("href").split("/")[5] === "pull") {
  159. alert.classList.remove("issues_comment");
  160. alert.classList.add("pull_request_comment");
  161. } else if (alert.classList.contains("gist")) {
  162. alert.classList.remove("gist");
  163. alert.classList.add("gist_" + alert.querySelector(".title span").textContent);
  164. }
  165. });
  166.  
  167. Array.forEach(ul.querySelectorAll("li"), function(li) {
  168. var c = li.querySelector(".count");
  169. if (li.filterClassNames[0] === "*") {
  170. c.textContent = container.querySelectorAll(".alert").length;
  171. } else {
  172. c.textContent = "0";
  173. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  174. if (li.filterClassNames.some(function(cl) { return alert.classList.contains(cl); })) {
  175. c.textContent = parseInt(c.textContent, 10) + 1;
  176. }
  177. });
  178. }
  179. });
  180.  
  181. sidebar.querySelector(".selected").dispatchEvent(new Event("click"));
  182. }, 1);
  183. });
  184. }
  185.  
  186. // init;
  187. addFilters();
  188.  
  189. // on pjax;
  190. unsafeWindow.$(document).on("pjax:success", addFilters);
  191.  
  192. })();