// ==UserScript==
// @name Asana Notifications favicon
// @grant none
// @namespace https://greasyfork.org/en/scripts/6118-asana-notifications-favicon
// @version 0.4
// @description Shows a red badge on the favicon if the inbox has unread messages
// @match https://app.asana.com/*
// @copyright 2014+, Jordi Gimenez Gamez
// ==/UserScript==
(function() {
// changes the favicon. avoids setting the same favicon twice.
function updateFavicon(unread) {
var readIcon = "https://d1gwm4cf8hecp4.cloudfront.net/images/favicon.ico";
var unreadIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjE8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjY0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj42NDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAg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";
if(this.lastState != unread) { // ensures we're not changing the icon all the time
var faviconLink = document.evaluate("//link[@rel='shortcut icon']").iterateNext();
faviconLink.href = unread ? unreadIcon : readIcon;
this.lastState = unread;
}
}
// checks read count and updates favicon if necessary
function updateRead() {
var unread = document.getElementsByClassName('inbox-count').length > 0;
updateFavicon(unread);
}
// observes any changes in the body and checks for changes in read count
function install() {
// this is costly. any change in ANY view will call the callback, but I can't find a better way without polling
if(this.observer === undefined) {
this.observer = new MutationObserver(function(mutations) {
updateRead();
// middle ground: disable observation for some time after an event
observer.disconnect();
setTimeout(install, 10000 /* 10 sec */);
});
}
this.observer.observe(document.body, { childList: true, subtree: true });
updateRead(); // make sure it's up to date before first event
}
// checks that the required Inbox Counter hack is enabled
function checkFeatures() {
function alertCSS(message) {
// original CSS here: http://jsfiddle.net/joshnh/FxfHc/
var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = "#cssalert{position:relative;z-index:9999;}#cssalert:hover:after{background:hsla(0,0%,0%,.8);border-radius:3px;color:#f6f6f6;content:'Click to dismiss';font:700 12px/30px sans-serif;height:30px;left:50%;margin-left:-60px;position:absolute;text-align:center;top:50px;width:120px}#cssalert:hover:before{border-bottom:10px solid hsla(0,0%,0%,.8);border-left:10px solid transparent;border-right:10px solid transparent;content:'';height:0;left:50%;margin-left:-10px;position:absolute;top:40px;width:0}#cssalert:target{display:none}.cssalert{background-color:#c4453c;background-image:-webkit-linear-gradient(135deg,transparent,transparent 25%,hsla(0,0%,0%,.05) 25%,hsla(0,0%,0%,.05) 50%,transparent 50%,transparent 75%,hsla(0,0%,0%,.05) 75%,hsla(0,0%,0%,.05));background-image:-moz-linear-gradient(135deg,transparent,transparent 25%,hsla(0,0%,0%,.1) 25%,hsla(0,0%,0%,.1) 50%,transparent 50%,transparent 75%,hsla(0,0%,0%,.1) 75%,hsla(0,0%,0%,.1));background-image:-ms-linear-gradient(135deg,transparent,transparent 25%,hsla(0,0%,0%,.1) 25%,hsla(0,0%,0%,.1) 50%,transparent 50%,transparent 75%,hsla(0,0%,0%,.1) 75%,hsla(0,0%,0%,.1));background-image:-o-linear-gradient(135deg,transparent,transparent 25%,hsla(0,0%,0%,.1) 25%,hsla(0,0%,0%,.1) 50%,transparent 50%,transparent 75%,hsla(0,0%,0%,.1) 75%,hsla(0,0%,0%,.1));background-image:linear-gradient(135deg,transparent,transparent 25%,hsla(0,0%,0%,.1) 25%,hsla(0,0%,0%,.1) 50%,transparent 50%,transparent 75%,hsla(0,0%,0%,.1) 75%,hsla(0,0%,0%,.1));background-size:20px 20px;box-shadow:0 5px 0 hsla(0,0%,0%,.1);color:#f6f6f6;display:block;font:700 16px/40px sans-serif;height:40px;position:absolute;text-align:center;text-decoration:none;top:-45px;width:100%;-webkit-animation:cssalert 1s ease forwards;-moz-animation:cssalert 1s ease forwards;-ms-animation:cssalert 1s ease forwards;-o-animation:cssalert 1s ease forwards;animation:cssalert 1s ease forwards}@-webkit-keyframes cssalert{0%{opacity:0}50%{opacity:1}100%{top:0}}@-moz-keyframes cssalert{0%{opacity:0}50%{opacity:1}100%{top:0}}@-ms-keyframes cssalert{0%{opacity:0}50%{opacity:1}100%{top:0}}@-o-keyframes cssalert{0%{opacity:0}50%{opacity:1}100%{top:0}}@keyframes cssalert{0%{opacity:0}50%{opacity:1}100%{top:0}}";
document.body.appendChild(css);
var div = document.createElement("div");
div.id = "cssalert";
var a = document.createElement("a");
a.className = "cssalert";
a.href = "#cssalert";
a.text = message;
div.appendChild(a);
document.body.insertBefore(div, document.body.firstChild); // at the top
}
if(!ExperimentalFeature.enabled({name:"inbox_new_count"})) {
alertCSS('In order for Asana Notifications favicon to work, you need to enable Inbox Counter in Account Settings > Hacks');
}
}
// let asana settle down
setTimeout(function() {
checkFeatures();
install();
}, 10000 /* 10 sec */);
}());