// Copyright 2006 Mihai Parparita. All Rights Reserved. // ==UserScript== // @name Gmail + Google Reader // @namespace http://persistent.info/ // @description Embeds Google Reader into Gmail by adding a "Feeds" link // @include http://mail.google.com/* // @include https://mail.google.com/* // @include http://www.google.com/reader/* // ==/UserScript== var readerFrameNode = null; var feedsContainerNode = null; var hiddenNodes = []; const GMAIL_STYLES = [ "#reader-frame {", " width: 100%;", " border: 0;", "}", ".reader-embedded #ft {", "display: none", "}", // Make currently selected item appear normal ".reader-embedded table.cv * {", " background: #fff;", " font-weight: normal", "}", // Make the feeds link appear selected ".reader-embedded #feeds-container {", " background: #c3d9ff;", " -moz-border-radius: 5px 0 0 5px;", " font-weight: bold;", " color: #00c;", "}", ].join("\n"); const READER_STYLES = [ "body {", " background: #fff", "}", "#nav,", "#logo-container,", "#global-info,", "#viewer-header {", " display: none !important;", "}", "", "#main {", " margin-top: 0;", "}", "", "#chrome {", " margin-left: 0;", "}" ].join("\n"); const READER_UNREAD_COUNT_URL = "http://www.google.com/reader/api/0/unread-count?" + "all=true&output=json&client=gm"; const READER_LIST_VIEW_URL = "http://www.google.com/reader/view/user/-/state/com.google/reading-list?" + "gmail-embed=true&view=list"; if (document.location.hostname == "mail.google.com") { injectGmail(); } else if (document.location.hostname == "www.google.com" && document.location.search.indexOf("gmail-embed") != -1) { injectReader(); } function injectGmail() { if (!getNode("nds") || !getNode("ds_inbox")) return; GM_addStyle(GMAIL_STYLES); var feedsNode = newNode("span"); feedsNode.className = "lk"; feedsNode.innerHTML = 'Feeds ' + ''; feedsNode.onclick = showReaderFrame; feedsContainerNode = newNode("div"); feedsContainerNode.className = "nl"; feedsContainerNode.id = "feeds-container"; feedsContainerNode.appendChild(feedsNode); var navNode = getNode("nds"); navNode.insertBefore(feedsContainerNode, navNode.childNodes[2]); window.addEventListener("resize", resizeReaderFrame, false); updateUnreadCount(); window.setInterval(updateUnreadCount, 5 * 60 * 1000); window.setInterval(checkFeedsParent, 1000); } function checkFeedsParent() { var navNode = getNode("nds"); if (feedsContainerNode.parentNode != navNode) { navNode.insertBefore(feedsContainerNode, navNode.childNodes[2]); } } function updateUnreadCount() { var unreadCountNode = getNode("reader-unread-count"); if (!unreadCountNode) return; GM_xmlhttpRequest({ method: "GET", url: READER_UNREAD_COUNT_URL, onload: function(details) { var data = eval("(" + details.responseText + ")"); var isUnread = false; for (var i = 0, unreadCountPair; unreadCountPair = data.unreadcounts[i]; i++) { if (unreadCountPair.id.indexOf("reading-list") != -1) { var count = unreadCountPair.count; if (count == 0) break; unreadCountNode.innerHTML = " (" + count + (count == data.max ? "+" : "") + ") "; isUnread = true; break; } } if (!isUnread) { unreadCountNode.innerHTML = ""; } } }); } function resizeReaderFrame() { if (!readerFrameNode) return; readerFrameNode.style.height = (window.innerHeight - readerFrameNode.offsetTop) + "px"; } function showReaderFrame(event) { var container = getNode("co"); addClass(document.body, "reader-embedded"); hiddenNodes = []; for (var i = container.firstChild; i; i = i.nextSibling) { hiddenNodes.push(i); i.style.display = "none"; } readerFrameNode = newNode("iframe"); readerFrameNode.src = READER_LIST_VIEW_URL; readerFrameNode.id = "reader-frame"; container.appendChild(readerFrameNode); container.parentNode.style.paddingRight = "0"; container.parentNode.style.paddingBottom = "0"; resizeReaderFrame(); // Make clicks outside the content area hide it getNode("nav").addEventListener("click", hideReaderFrame, false); // Since we're in a child of the "nav" element, the above handler will get // triggered immediately unless we stop this event from propagating event.stopPropagation(); return false; } function hideReaderFrame() { var container = getNode("co"); container.removeChild(readerFrameNode); readerFrameNode = null; for (var i=0; i < hiddenNodes.length; i++) { hiddenNodes[i].style.display = ""; } getNode("nav").removeEventListener("click", hideReaderFrame, false); removeClass(document.body, "reader-embedded"); container.parentNode.style.paddingRight = "1ex"; container.parentNode.style.paddingBottom = "1ex"; return true; } function injectReader() { GM_addStyle(READER_STYLES); } // Shorthand function newNode(type) {return unsafeWindow.document.createElement(type);} function newText(text) {return unsafeWindow.document.createTextNode(text);} function getNode(id) {return unsafeWindow.document.getElementById(id);} function hasClass(node, className) { return className in getClassMap(node); } function addClass(node, className) { if (hasClass(node, className)) return; node.className += " " + className; } function removeClass(node, className) { var classMap = getClassMap(node); if (!(className in classMap)) return; delete classMap[className]; var newClassList = []; for (var className in classMap) { newClassList.push(className); } node.className = newClassList.join(" "); } function getClassMap(node) { var classMap = {}; var classNames = node.className.split(/\s+/); for (var i = 0; i < classNames.length; i++) { classMap[classNames[i]] = true; } return classMap; }