// ==UserScript==
// @name Github News Feed Filter
// @namespace https://github.com/jerone/UserScripts
// @description Add filters for Github homepage news feed items
// @author jerone
// @copyright 2014+, jerone (http://jeroenvanwarmerdam.nl)
// @license GNU GPLv3
// @homepage https://github.com/jerone/UserScripts/tree/master/Github_News_Feed_Filter
// @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_News_Feed_Filter
// @include https://github.com/
// @include https://github.com/?*
// @include https://github.com/orgs/*/dashboard
// @include https://github.com/orgs/*/dashboard?*
// @include https://github.com/*tab=activity*
// @version 6.1
// @grant none
// ==/UserScript==
/* global Event */
(function() {
var FILTERS = [
{ id: "*", text: "All news feed", icon: "octicon-radio-tower", classNames: ["*"] },
{
id: "issues", text: "Issues", icon: "octicon-issue-opened", classNames: ["issues_opened", "issues_closed", "issues_reopened", "issues_comment"], subFilters: [
{ id: "issues opened", text: "Opened", icon: "octicon-issue-opened", classNames: ["issues_opened"] },
{ id: "issues closed", text: "Closed", icon: "octicon-issue-closed", classNames: ["issues_closed"] },
{ id: "issues reopened", text: "Reopened", icon: "octicon-issue-reopened", classNames: ["issues_reopened"] },
{ id: "issues comments", text: "Comments", icon: "octicon-comment-discussion", classNames: ["issues_comment"] }
]
},
{
id: "commits", text: "Commits", icon: "octicon-git-commit", classNames: ["push", "commit_comment"], subFilters: [
{ id: "commits pushed", text: "Pushed", icon: "octicon-git-commit", classNames: ["push"] },
{ id: "commits comments", text: "Comments", icon: "octicon-comment-discussion", classNames: ["commit_comment"] }
]
},
{
id: "pr", text: "Pull Requests", icon: "octicon-git-pull-request", classNames: ["pull_request_opened", "pull_request_closed", "pull_request_merged", "pull_request_comment"], subFilters: [
{ id: "pr opened", text: "Opened", icon: "octicon-git-pull-request", classNames: ["pull_request_opened"] },
{ id: "pr closed", text: "Closed", icon: "octicon-git-pull-request-abandoned", classNames: ["pull_request_closed"] },
{ id: "pr merged", text: "Merged", icon: "octicon-git-merge", classNames: ["pull_request_merged"] },
{ id: "pr comments", text: "Comments", icon: "octicon-comment-discussion", classNames: ["pull_request_comment"] }
]
},
{
id: "repo", text: "Repo", icon: "octicon-repo", classNames: ["create", "public", "fork", "branch_create", "branch_delete", "tag_add", "tag_remove", "release", "delete"], subFilters: [
{ id: "repo created", text: "Created", icon: "octicon-repo-create", classNames: ["create"] },
{ id: "repo public", text: "Public", icon: "octicon-repo-push", classNames: ["public"] },
{ id: "repo forked", text: "Forked", icon: "octicon-repo-forked", classNames: ["fork"] },
{ id: "repo deleted", text: "Deleted", icon: "octicon-repo-delete", classNames: ["delete"] },
{ id: "repo released", text: "Release", icon: "octicon-repo-pull", classNames: ["release"] },
{
id: "repo branched", text: "Branch", icon: "octicon-git-branch", classNames: ["branch_create", "branch_delete"], subFilters: [
{ id: "repo branch created", text: "Created", icon: "octicon-git-branch-create", classNames: ["branch_create"] },
{ id: "repo branch deleted", text: "Deleted", icon: "octicon-git-branch-delete", classNames: ["branch_delete"] }
]
},
{
id: "repo tagged", text: "Tag", icon: "octicon-tag", classNames: ["tag_add", "tag_remove"], subFilters: [
{ id: "repo tag added", text: "Added", icon: "octicon-tag-add", classNames: ["tag_add"] },
{ id: "repo tag removed", text: "Removed", icon: "octicon-tag-remove", classNames: ["tag_remove"] }
]
}
]
},
{
id: "user", text: "User", icon: "octicon-person", classNames: ["watch_started", "member_add", "team_add"], subFilters: [
{ id: "user starred", text: "Starred", icon: "octicon-star", classNames: ["watch_started"] },
{ id: "user added", text: "Member added", icon: "octicon-person-add", classNames: ["member_add", "team_add"] }
]
},
{
id: "wiki", text: "Wiki", icon: "octicon-book", classNames: ["wiki_created", "wiki_edited"], subFilters: [
{ id: "wiki created", text: "Created", icon: "octicon-plus", classNames: ["wiki_created"] },
{ id: "wiki edited", text: "Edited", icon: "octicon-book", classNames: ["wiki_edited"] }
]
},
{
id: "gist", text: "Gist", icon: "octicon-gist", classNames: ["gist_created", "gist_updated"], subFilters: [
{ id: "gist created", text: "Created", icon: "octicon-gist-new", classNames: ["gist_created"] },
{ id: "gist updated", text: "Updated", icon: "octicon-gist", classNames: ["gist_updated"] }
]
}
// Possible other classes: follow
];
var datasetId = "githubNewsFeedFilterId";
function proxy(fn) {
return function() {
var that = this;
return function(e) {
var args = that.slice(0); // clone;
args.unshift(e); // prepend event;
fn.apply(this, args);
};
}.call([].slice.call(arguments, 1));
}
function addStyle(css) {
var node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(css));
document.head.appendChild(node);
}
addStyle("\
.GitHubNewsFeedFilter .count { margin-right: 15px; }\
\
.GitHubNewsFeedFilter .filter-list .filter-list .mini-repo-list-item { padding-left: 40px; border-top: 1px dashed #E5E5E5; }\
.GitHubNewsFeedFilter .filter-list .filter-list .filter-list .mini-repo-list-item { padding-left: 50px; }\
\
.GitHubNewsFeedFilter .filter-list-item > ul { display: none; }\
.GitHubNewsFeedFilter .filter-list-item.open > ul { display: block; }\
\
.GitHubNewsFeedFilter .private { font-weight: bold; }\
\
.GitHubNewsFeedFilter .stars .octicon { position: absolute; right: -4px; }\
.GitHubNewsFeedFilter .filter-list-item.open > a > .stars > .octicon:before { content: '\\f05b'; }\
\
.no-alerts { font-style: italic; }\
")
function addFilterMenu(filters, parent, container, sidebar, main) {
var ul = document.createElement("ul");
ul.classList.add("filter-list");
if (main) {
ul.classList.add("boxed-group-inner", "mini-repo-list");
}
parent.appendChild(ul);
filters.forEach(function(subFilter) {
var li = addFilterMenuItem(subFilter, ul, container, sidebar);
if (subFilter.subFilters) {
addFilterMenu(subFilter.subFilters, li, container, sidebar, false);
}
});
}
function addFilterMenuItem(filter, parent, container, sidebar) {
var a = document.createElement("a");
a.classList.add("mini-repo-list-item", "css-truncate");
a.setAttribute("href", "/");
a.setAttribute("title", filter.classNames.join(" & "));
a.dataset[datasetId] = filter.id;
// Filter icon;
var i = document.createElement("span");
i.classList.add("repo-icon", "octicon", filter.icon);
a.appendChild(i);
// Filter count & sub list arrow;
var s = document.createElement("span");
s.classList.add("stars");
var c = document.createElement("span");
c.classList.add("count");
c.appendChild(document.createTextNode("0"));
s.appendChild(c);
if (filter.subFilters) {
s.appendChild(document.createTextNode(" "));
var o = document.createElement("span");
o.classList.add("octicon", "octicon-triangle-left");
s.appendChild(o);
}
a.appendChild(s);
// Filter text;
a.appendChild(document.createTextNode(filter.text));
a.addEventListener("click", proxy(function(e, classNames) {
e.preventDefault();
// Show/hide message about no alerts;
var any = false,
all = classNames[0] === "*",
some = function(alert) { return classNames.some(function(cl) { return alert.classList.contains(cl); }); };
Array.forEach(container.querySelectorAll(".alert"), function(alert) {
alert.style.display = (all || some(alert)) && (any = true) ? "block" : "none";
});
var none = container.querySelector(".no-alerts");
if (any && none) {
none.parentNode.removeChild(none);
} else if (!any && !none) {
none = document.createElement("div");
none.classList.add("no-alerts", "protip");
none.appendChild(document.createTextNode("No feed items for this filter. Please select another filter."));
container.insertBefore(none, container.firstElementChild.nextElementSibling);
}
// Open/close sub list;
Array.forEach(sidebar.querySelectorAll(".GitHubNewsFeedFilter .open"), function(item) { item.classList.remove("open"); });
showParentMenu(this);
this.parentNode.classList.add("open");
// Give it a colored background;
Array.forEach(sidebar.querySelectorAll(".GitHubNewsFeedFilter .private"), function(m) { m.classList.remove("private"); });
this.parentNode.classList.add("private");
// Push filter to url;
if (this.dataset[datasetId] !== "*") {
var urlSearch = "filter=" + encodeURIComponent(this.dataset[datasetId]);
history.pushState(null, null, location.search && /filter=[^&]*/g.test(location.search)
? location.href.replace(/filter=[^&]*/g, urlSearch)
: location.href + (location.search ? "&" : "?") + urlSearch);
} else {
history.pushState(null, null, location.href.replace(/(filter=[^&]*&|\?filter=[^&]*$|&filter=[^&]*)/g, "")); // http://regexr.com/398lv
}
}, filter.classNames));
var li = document.createElement("li");
li.classList.add("filter-list-item");
li.appendChild(a);
li.filterClassNames = filter.classNames;
parent.appendChild(li);
return li;
}
// Traverse back up the tree to open sub lists;
function showParentMenu(menuItem) {
var parentMenuItem = menuItem.parentNode;
if (parentMenuItem.classList.contains("filter-list-item")) {
parentMenuItem.classList.add("open");
showParentMenu(parentMenuItem.parentNode);
}
}
function pageUpdate(container, sidebar, wrapper) {
// Fix filter identification;
Array.forEach(container.querySelectorAll(".alert"), function(alert) {
if (alert.getElementsByClassName("octicon-git-branch").length > 0 && !alert.classList.contains("fork")) {
alert.classList.remove("create");
alert.classList.add("branch_create");
} else if (alert.getElementsByClassName("octicon-git-branch-delete").length > 0) {
alert.classList.remove("delete");
alert.classList.add("branch_delete");
} else if (alert.getElementsByClassName("octicon-tag").length > 0 && !alert.classList.contains("release")) {
alert.classList.remove("create");
alert.classList.add("tag_add");
} else if (alert.getElementsByClassName("octicon-tag-remove").length > 0) {
alert.classList.remove("delete");
alert.classList.add("tag_remove");
} else if (alert.getElementsByClassName("octicon-git-pull-request").length > 0) {
if (alert.classList.contains("issues_opened")) {
alert.classList.remove("issues_opened");
alert.classList.add("pull_request_opened");
} else if (alert.classList.contains("issues_closed")) {
alert.classList.remove("issues_closed");
if (!!~alert.querySelector('.title').textContent.indexOf('merged pull request')) {
alert.classList.add("pull_request_merged");
} else {
alert.classList.add("pull_request_closed");
}
}
} else if (alert.classList.contains("issues_comment") && alert.querySelectorAll(".title a")[1].href.split("/")[5] === "pull") {
alert.classList.remove("issues_comment");
alert.classList.add("pull_request_comment");
} else if (alert.classList.contains("gollum")) {
alert.classList.remove("gollum");
if (!!~alert.querySelector('.title').textContent.indexOf(" created the ")) {
alert.classList.add("wiki_created");
} else if (!!~alert.querySelector('.title').textContent.indexOf(" edited the ")) {
alert.classList.add("wiki_edited");
}
} else if (alert.classList.contains("gist")) {
alert.classList.remove("gist");
alert.classList.add("gist_" + alert.querySelector(".title span").textContent);
}
});
// Update filter counts;
Array.forEach(wrapper.querySelectorAll("li"), function(li) {
var c = li.querySelector(".count");
if (li.filterClassNames[0] === "*") {
c.textContent = container.querySelectorAll(".alert").length;
} else {
c.textContent = "0";
Array.forEach(container.querySelectorAll(".alert"), function(alert) {
if (li.filterClassNames.some(function(cl) { return alert.classList.contains(cl); })) {
c.textContent = parseInt(c.textContent, 10) + 1;
}
});
}
});
// Apply filter from url;
var filter = /filter=[^&]*/g.test(location.search)
? decodeURIComponent(/filter=([^&]*)/g.exec(location.search)[1])
: "*";
wrapper.querySelector('.GitHubNewsFeedFilter [data-github-news-feed-filter-id="' + filter + '"]').dispatchEvent(new Event("click"));
}
function addFilters() {
var container = document.querySelector(".news");
if (!container) { return; }
var sidebar = document.querySelector(".dashboard-sidebar") || document.querySelector(".column.one-fourth.vcard");
var rule = document.createElement("div");
rule.classList.add("rule");
sidebar.insertBefore(rule, sidebar.firstChild);
var wrapper = document.createElement("div");
wrapper.classList.add("GitHubNewsFeedFilter", "boxed-group", "flush");
sidebar.insertBefore(wrapper, sidebar.firstChild);
var header = document.createElement("h3");
header.appendChild(document.createTextNode("News feed filter"));
wrapper.appendChild(header);
addFilterMenu(FILTERS, wrapper, container, sidebar, true);
pageUpdate(container, sidebar, wrapper);
// Update on clicking "More"-button;
new MutationObserver(function() {
pageUpdate(container, sidebar, wrapper);
}).observe(container, { childList: true });
}
// init;
addFilters();
})();