Github News Feed Filter

Add filters for Github homepage news feed items

当前为 2014-05-02 提交的版本,查看 最新版本

  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 5.0
  11. // @grant none
  12. // ==/UserScript==
  13. /* global unsafeWindow,Event */
  14.  
  15. (function() {
  16.  
  17. var FILTERS = [
  18. { text: "All News Feed", icon: "octicon-radio-tower", classNames: ["*"] },
  19. {
  20. text: "Issues", icon: "octicon-issue-opened", classNames: ["issues_opened", "issues_closed", "issues_reopened", "issues_comment"], subFilters: [
  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. { text: "Comments", icon: "octicon-comment-discussion", classNames: ["issues_comment"] }
  25. ]
  26. },
  27. {
  28. text: "Commits", icon: "octicon-git-commit", classNames: ["push", "commit_comment"], subFilters: [
  29. { text: "Pushed", icon: "octicon-git-commit", classNames: ["push"] },
  30. { text: "Comments", icon: "octicon-comment-discussion", classNames: ["commit_comment"] }
  31. ]
  32. },
  33. {
  34. text: "Pull Requests", icon: "octicon-git-pull-request", classNames: ["pull_request_opened", "pull_request_closed", "pull_request_merged", "pull_request_comment"], subFilters: [
  35. { text: "Opened", icon: "octicon-git-pull-request", classNames: ["pull_request_opened"] },
  36. { text: "Closed", icon: "octicon-git-pull-request-abandoned", classNames: ["pull_request_closed"] },
  37. { text: "Merged", icon: "octicon-git-merge", classNames: ["pull_request_merged"] },
  38. { text: "Comments", icon: "octicon-comment-discussion", classNames: ["pull_request_comment"] }
  39. ]
  40. },
  41. {
  42. text: "Repo", icon: "octicon-repo", classNames: ["create", "public", "fork", "branch_create", "branch_delete", "tag_add", "tag_remove", "release", "delete"], subFilters: [
  43. { text: "Created", icon: "octicon-repo-create", classNames: ["create"] },
  44. { text: "Public", icon: "octicon-repo-push", classNames: ["public"] },
  45. { text: "Forked", icon: "octicon-repo-forked", classNames: ["fork"] },
  46. {
  47. text: "Branched", icon: "octicon-git-branch", classNames: ["branch_create", "branch_delete"], subFilters: [
  48. { text: "Created", icon: "octicon-git-branch-create", classNames: ["branch_create"] },
  49. { text: "Deleted", icon: "octicon-git-branch-delete", classNames: ["branch_delete"] }
  50. ]
  51. },
  52. {
  53. text: "Tagged", icon: "octicon-tag", classNames: ["tag_add", "tag_remove"], subFilters: [
  54. { text: "Added", icon: "octicon-tag-add", classNames: ["tag_add"] },
  55. { text: "Removed", icon: "octicon-tag-remove", classNames: ["tag_remove"] }
  56. ]
  57. },
  58. { text: "Released", icon: "octicon-repo-pull", classNames: ["release"] },
  59. { text: "Deleted", icon: "octicon-repo-delete", classNames: ["delete"] }
  60. ]
  61. },
  62. {
  63. text: "User", icon: "octicon-person", classNames: ["watch_started", "member_add", "team_add"], subFilters: [
  64. { text: "Starred", icon: "octicon-star", classNames: ["watch_started"] },
  65. { text: "Member added", icon: "octicon-person-add", classNames: ["member_add", "team_add"] }
  66. ]
  67. },
  68. { text: "Wiki", icon: "octicon-book", classNames: ["gollum"] },
  69. {
  70. text: "Gist", icon: "octicon-gist", classNames: ["gist_created", "gist_updated"], subFilters: [
  71. { text: "Created", icon: "octicon-gist-new", classNames: ["gist_created"] },
  72. { text: "Updated", icon: "octicon-gist", classNames: ["gist_updated"] }
  73. ]
  74. }
  75. // Possible other classes: follow
  76. ];
  77.  
  78. function proxy(fn) {
  79. return function() {
  80. var that = this;
  81. return function(e) {
  82. var args = that.slice(0); // clone;
  83. args.unshift(e); // prepend event;
  84. fn.apply(this, args);
  85. };
  86. }.call([].slice.call(arguments, 1));
  87. }
  88.  
  89. function addFilterMenu(filters, parent, container, sidebar, main) {
  90. var ul = document.createElement("ul");
  91. ul.classList.add("filter-list");
  92. if (!main) {
  93. ul.classList.add("small");
  94. ul.style.marginLeft = "10px";
  95. ul.style.display = "none";
  96. }
  97. parent.appendChild(ul);
  98.  
  99. filters.forEach(function(subFilter) {
  100. var li = addFilterMenuItem(subFilter, ul, container, sidebar);
  101.  
  102. if (subFilter.subFilters) {
  103. addFilterMenu(subFilter.subFilters, li, container, sidebar, false);
  104. }
  105. });
  106. }
  107.  
  108. function addFilterMenuItem(filter, parent, container, sidebar) {
  109. var a = document.createElement("a");
  110. a.classList.add("filter-item");
  111. a.setAttribute("href", "/");
  112. a.setAttribute("title", filter.classNames.join(" & "));
  113. if (filter.classNames[0] === "*") {
  114. a.classList.add("selected");
  115. a.style.fontWeight = "bold";
  116. }
  117.  
  118. var s = document.createElement("span");
  119. s.classList.add("octicon", filter.icon);
  120. s.style.marginRight = "10px";
  121. s.style.cssFloat = "left";
  122. s.style.minWidth = "16px";
  123. a.appendChild(s);
  124.  
  125. var c = document.createElement("span");
  126. c.classList.add("count");
  127. c.appendChild(document.createTextNode("0"));
  128. a.appendChild(c);
  129.  
  130. a.appendChild(document.createTextNode(filter.text));
  131.  
  132. a.addEventListener("click", proxy(function(e, classNames) {
  133. e.preventDefault();
  134.  
  135. var any = false,
  136. all = classNames[0] === "*",
  137. some = function(alert) { return classNames.some(function(cl) { return alert.classList.contains(cl); }); };
  138. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  139. alert.style.display = (all || some(alert)) && (any = true) ? "block" : "none";
  140. });
  141. var none = container.querySelector(".no-alerts");
  142. if (any && none) {
  143. none.parentNode.removeChild(none);
  144. } else if (!any && !none) {
  145. none = document.createElement("div");
  146. none.classList.add("no-alerts");
  147. none.style.padding = "0 0 1em 45px";
  148. none.style.fontStyle = "italic";
  149. none.appendChild(document.createTextNode("No feed items for this filter. Press the button below to load more items..."));
  150. container.insertBefore(none, container.firstChild);
  151. }
  152.  
  153. Array.forEach(sidebar.querySelectorAll(".filter-list.small"), function(ul) { ul.style.display = "none"; });
  154. showParentMenu(a.parentNode);
  155. var subMenu = a.parentNode.querySelector("ul");
  156. if (subMenu) { subMenu.style.display = "block"; }
  157.  
  158. Array.forEach(sidebar.querySelectorAll(".selected"), function(m) { m.classList.remove("selected"); });
  159. this.classList.add("selected");
  160. }, filter.classNames));
  161.  
  162. var li = document.createElement("li");
  163. li.appendChild(a);
  164. li.filterClassNames = filter.classNames;
  165.  
  166. parent.appendChild(li);
  167.  
  168. return li;
  169. }
  170.  
  171. function showParentMenu(menuItem) {
  172. var parentMenuItem = menuItem.parentNode;
  173. if (parentMenuItem.classList.contains("filter-list")) {
  174. parentMenuItem.style.display = "block";
  175. showParentMenu(parentMenuItem.parentNode);
  176. }
  177. }
  178.  
  179. function addFilters() {
  180. var container = document.querySelector(".news");
  181. if (!container) { return; }
  182.  
  183. var sidebar = document.querySelector(".dashboard-sidebar");
  184.  
  185. var rule = document.createElement("div");
  186. rule.classList.add("rule");
  187. sidebar.insertBefore(rule, sidebar.firstChild);
  188.  
  189. var div = document.createElement("div");
  190. sidebar.insertBefore(div, sidebar.firstChild);
  191.  
  192. addFilterMenu(FILTERS, div, container, sidebar, true);
  193.  
  194. // update on clicking "More"-button;
  195. unsafeWindow.$.pageUpdate(function() {
  196. window.setTimeout(function() {
  197. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  198. if (alert.getElementsByClassName("octicon-git-branch-create").length > 0) {
  199. alert.classList.remove("create");
  200. alert.classList.add("branch_create");
  201. } else if (alert.getElementsByClassName("octicon-git-branch-delete").length > 0) {
  202. alert.classList.remove("delete");
  203. alert.classList.add("branch_delete");
  204. } else if (alert.getElementsByClassName("octicon-tag-add").length > 0) {
  205. alert.classList.remove("create");
  206. alert.classList.add("tag_add");
  207. } else if (alert.getElementsByClassName("octicon-tag-remove").length > 0) {
  208. alert.classList.remove("delete");
  209. alert.classList.add("tag_remove");
  210. } else if (alert.getElementsByClassName("octicon-git-pull-request").length > 0) {
  211. alert.classList.remove("issues_opened", "issues_closed");
  212. if (alert.querySelector(".title span").textContent.toUpperCase() === "OPENED") { // English localisation;
  213. alert.classList.add("pull_request_opened");
  214. } else if (alert.querySelector(".title span").textContent.toUpperCase() === "MERGED") { // English localisation;
  215. alert.classList.add("pull_request_merged");
  216. } else if (alert.querySelector(".title span").textContent.toUpperCase() === "CLOSED") { // English localisation;
  217. alert.classList.add("pull_request_closed");
  218. }
  219. } else if (alert.classList.contains("issues_comment") && alert.querySelectorAll(".title a")[1].getAttribute("href").split("/")[5] === "pull") {
  220. alert.classList.remove("issues_comment");
  221. alert.classList.add("pull_request_comment");
  222. } else if (alert.classList.contains("gist")) {
  223. alert.classList.remove("gist");
  224. alert.classList.add("gist_" + alert.querySelector(".title span").textContent);
  225. }
  226. });
  227.  
  228. Array.forEach(div.querySelectorAll("li"), function(li) {
  229. var c = li.querySelector(".count");
  230. if (li.filterClassNames[0] === "*") {
  231. c.textContent = container.querySelectorAll(".alert").length;
  232. } else {
  233. c.textContent = "0";
  234. Array.forEach(container.querySelectorAll(".alert"), function(alert) {
  235. if (li.filterClassNames.some(function(cl) { return alert.classList.contains(cl); })) {
  236. c.textContent = parseInt(c.textContent, 10) + 1;
  237. }
  238. });
  239. }
  240. });
  241.  
  242. sidebar.querySelector(".selected").dispatchEvent(new Event("click"));
  243. }, 1);
  244. });
  245. }
  246.  
  247. // init;
  248. addFilters();
  249.  
  250. // on pjax;
  251. unsafeWindow.$(document).on("pjax:success", addFilters);
  252.  
  253. })();