// --------- FaceBook ----------
// @url http://www.new.facebook.com
// @name Facebook
// @desc This script notifies about every new story in the Live Feed.
// version 1.9.1
/**
* This script finds every new story (by its header text) in the
* Lives Feed and then appends this story to the notification.
* When all new stories are extracted, user gets notifiaction about his/her new stories.
* The procedure of reloading the iframe called every "reloadInterval" seconds
*/
// This variable is visible to the facebook page! It stores executable scripts from
// Lives Feeds. So when you clicking on some link in notification message that needs to run
// javascript, the script is taken from here (main bubble scope) otherwise the script in
// notification message scope will fail to run.
var executableScriptForBubbles;
// anonymous function that wraps whole script to prevent naming conflicts
new function() {
//SSB.console.init("debug","iframe");
log = SSB.console.debug;
// ----- Constants -----
var FACEBOOK_COMMON_CSS_URL = "http://static.ak.fbcdn.net/rsrc.php/z5GT6/pkg/102/127996/css/common.css.pkg.php";
var FACEBOOK_FEED_CSS_URL = "http://static.ak.fbcdn.net/rsrc.php/zBKH5/pkg/102/126119/css/feed.css.pkg.php";
var CHAT_CHECKING_INTERVAL = "5"; // in seconds
var IE_VERSION = /MSIE ([\d\.]+)/.exec(navigator.userAgent)[1];
var bubblesTrckr = null;
// ----- Configuration Variables -----
var maxNotificationHegiht = 277; // Max height of the notification message.
// If the notification content is shorter than
// maxNotificationHeight, then the only the needed
// height will be used.
var reloadIntervalDefault = 120;
SSB.prefs.pane.init([{type:'string',defVal:reloadIntervalDefault,name:'reloadInterval',title:'News Feed checking interval (in seconds)'}]);
var reloadInterval = SSB.prefs.getValue("reloadInterval");
if (reloadInterval == null) reloadInterval = reloadIntervalDefault;
SSB.prefs.setValue("reloadInterval", reloadInterval);
createBubblesTrckr();
var URL = window.location.href;
var facebookURL = URL.match(/(http:\/\/[^\/]+)\//)[1];
var bubblesBugNewsFeedContainer = null;
var doc = document;
testStylesAdded = false;
function error(err, inFunction) {
var msg = "Error occurred in "+inFunction+"\n";
for (each in err) {
msg += each + ": " + err[each] + "\n";
}
SSB.console.error(msg);
}
var bubblesBugNewsFeedContainer = doc.createElement("div");
bubblesBugNewsFeedContainer.style.position = "absolute";
bubblesBugNewsFeedContainer.style.backgroundColor = "#FFFFFF";
bubblesBugNewsFeedContainer.style.top = "-1000px";
bubblesBugNewsFeedContainer.style.left = "-1000px";
bubblesBugNewsFeedContainer.style.width = "222px";
bubblesBugNewsFeedContainer.style.height = "277px";
bubblesBugNewsFeedContainer.style.overflow = "hidden";
bubblesBugNewsFeedContainer.id = "bubblesBugNewsFeedContainer";
doc.body.appendChild(bubblesBugNewsFeedContainer);
var testDiv = doc.createElement("div");
testDiv.style.backgroundColor = "#FFFFFF";
testDiv.style.position = "absolute";
testDiv.style.top = "-10000px";
testDiv.style.left = "-10000px";
testDiv.style.zIndex = "100";
doc.body.appendChild(testDiv);
sendAjaxFeedRequest();
if (IE_VERSION >= 7) {
//window.setInterval(checkChat,CHAT_CHECKING_INTERVAL);
}
function sendAjaxFeedRequest() {
try {
log("Entered sendAjaxFeedRequest()");
var xmlHttp = getXmlHttpObject();
xmlHttp.onreadystatechange = function() {
// in IE readyState can be: uninitialized/loading/loaded/interactive/complete
// for xmlHttpRequest it is 0/1/2/3/4
if (xmlHttp.readyState == 4) {
log("xmlHttp.readyState == 4");
try {
var JSONString = xmlHttp.responseText.match(/\{[\s\S]*/)[0];
var JSON = bubblesJSON.parse(JSONString)
log("Parsed received JSON");
bubblesBugNewsFeedContainer.innerHTML = JSON.payload.html;
checkNewsFeed();
} catch(e) {
error(e,"checkNewsFeed()");
}
}
}
var url = facebookURL+"/ajax/feed.php?tab=2";
xmlHttp.open("POST",url,true);
xmlHttp.send(null);
log('Sent XmlHttpRequest to the '+url);
} catch(e) {
error(e,"sendAjaxFeedRequest()");
}
reloadInterval = SSB.prefs.getValue("reloadInterval");
window.setTimeout(sendAjaxFeedRequest, reloadInterval*1000);
}
/**
* Check for new feeds.
*/
function checkNewsFeed() {
log('Entered checkNewsFeed()');
var lastHeader = SSB.prefs.getValue("lastHeader");
// If there was no lastHeader saved in prefs (Facebook bubbles runing for the
// first time) then lastHeader will be null, and notification will include all
// stories.
// Uncomment to simulate constant notification
//lastHeader = null;
log("lastHeader = '" + lastHeader +"'");
var storiesContainer = firstChildElement(bubblesBugNewsFeedContainer)
var headerTitleWrapper;
var currentHeader;
var isLastHeaderSet = false;
var numberOfNewStories = 0;
var currentElement = firstChildElement(storiesContainer);
var firstElementToAdd = currentElement;
var lastAddedElement = null;
var oneLinerCluster = null;
var docFragment = doc.createElement("div");
var currentFragment = docFragment;
while (currentElement) {
log("start of while: currentElement tagName = "+currentElement.tagName+" currentElement className = "+currentElement.className+" id = "+currentElement.id);
currentHeader = null;
// Try to get headerTitleWrapper of the current element
// Check if currentElement has class="feed_item", if is then process the
// currentElement.
if (hasClassName(currentElement, "feed_item")) {
// Get header of the story
headerTitleWrapper = getElementsByClassName("header_title_wrapper",currentElement);
// Check that headerTitleWrapper is found.
if (headerTitleWrapper.length > 0) {
headerTitleWrapper = headerTitleWrapper[0];
var status_body = getElementsByClassName("status_body",headerTitleWrapper);
if (status_body.length > 0) {
currentHeader = status_body[0].innerText;
} else {
currentHeader = headerTitleWrapper.innerText;
}
log("-currentHeader = "+currentHeader);
}
}
if (currentHeader !== null) {
// If this is first iteration:
if (!isLastHeaderSet) {
// Set the lastHeader value of SSB.prefs to the first story header
SSB.prefs.setValue("lastHeader", currentHeader);
log("-SSB.prefs.setValue - lastHeader = "+currentHeader);
isLastHeaderSet = true;
// If there was no lastHeader saved in prefs (Facebook bubbles runing for the first time)
// then do not show the notification;
// if (lastHeader == null)
// lastHeader = currentHeader;
// If last story is visible (The user is on home page and sees the last story)
// then do not show the notification.
if ( isLastStoryVisible(currentHeader) ) {
log("-isLastStoryVisible(currentHeader) = true");
lastHeader = currentHeader;
log("-lastHeader = currentHeader");
}
}
// If currentHeader not equal to last saved header, then add this story
// to the notification.
if (currentHeader != lastHeader) {
numberOfNewStories++;
do {
log("--currentHeader != lastHeader");
if (lastAddedElement != null) {
log("--lastAddedElement = "+lastAddedElement.className);
var elementToAdd = nextSiblingElement(lastAddedElement);
log("--elementToAdd = "+elementToAdd.className);
}
else {
log("--lastAddedElement == null");
var elementToAdd = firstElementToAdd;
log("--elementToAdd = "+elementToAdd.className);
}
if ( !hasClassName(elementToAdd, "social_ad") ) {
if (elementToAdd == oneLinerCluster) {
log("---elementToAdd isCluster! = "+elementToAdd.className);
var clonedElementToAdd = elementToAdd.cloneNode(false);
currentFragment = currentFragment.appendChild(clonedElementToAdd);
elementToAdd = firstChildElement(oneLinerCluster);
log("---elementToAdd first child of Cluster = "+elementToAdd.className);
}
var clonedElementToAdd = elementToAdd.cloneNode(true);
currentFragment.appendChild(clonedElementToAdd);
}
else { log("---elementToAdd (is ad) = "+elementToAdd.className); }
lastAddedElement = elementToAdd;
log("--lastAddedElement = "+elementToAdd.className+" id = "+elementToAdd.id);
} while (lastAddedElement != currentElement);
currentElement = nextSiblingElement(currentElement);
} else {
log("--currentHeader == lastHeader");
currentElement = null;
oneLinerCluster = null;
}
} else {
// If currentElement does not have class="feed_item", then check maybe it is
// it have class="one_liner_cluster" if is, then jump to the first child of
// one_liner_cluster, otherwise proceed to the next sibling element.
log("-currentHeader == null");
if (hasClassName(currentElement, "one_liner_cluster")) {
oneLinerCluster = currentElement;
currentElement = firstChildElement(oneLinerCluster);
} else {
currentElement = nextSiblingElement(currentElement);
}
}
if (!currentElement && oneLinerCluster) {
log("-end of cluster!");
var elementToAdd = nextSiblingElement(lastAddedElement);
while (elementToAdd) {
log("--elementToAdd = "+elementToAdd.className);
var clonedElementToAdd = elementToAdd.cloneNode(true);
currentFragment.appendChild(clonedElementToAdd);
elementToAdd = nextSiblingElement(elementToAdd);
}
currentFragment = docFragment;
lastAddedElement = oneLinerCluster;
log("-lastAddedElement = "+lastAddedElement.className);
currentElement = nextSiblingElement(oneLinerCluster);
oneLinerCluster = null;
}
log("End of iteration");
}
log("num of childs = "+docFragment.childNodes.length);
log("numberOfNewStories = "+numberOfNewStories);
if ( numberOfNewStories > 0 ) {
if (bubblesTrckr) {
try {
bubblesTrckr._trackEvent("Notifications", "Facebook");
} catch(e) {};
}
var url = "http://bubbleshq.com/client/facebookNotification/";
docFragment = removeFeedOptions(docFragment);
var stories = docFragment.innerHTML;
stories = stories.replace(/href="([^"]*)"/g,'href="JavaScript:runHREF(\'$1\');"');
log("Replaced href elements");
executableScriptForBubbles = new Array();
stories = stories.replace(/onload="[^"]*"/g, "");
stories = stories.replace(/onmousemove="[^"]*"/g, "");
stories = stories.replace(/onmouseout="[^"]*"/g, "");
stories = stories.replace(/onclick="([^"]*)"/g, replaceOnClick);
function replaceOnClick(matchedSubstring, backReference, offset, searchedString) {
var index = executableScriptForBubbles.push(backReference) - 1;
var result = 'onclick="runJavaScript('+index+')"';
return result;
}
log("Replaced onclick attributes");
var userName = doc.getElementById("fb_menu_account").innerText;
userName = userName.match(/(\w+)[\s\S]?/)[1];
log("userName = "+userName);
var bodyHtml = getStringFromFunction(bodyHTML);
bodyHtml = bodyHtml.replace(/__STORIES__/, stories);
bodyHtml = bodyHtml.replace(/__URL__/g, url);
bodyHtml = bodyHtml.replace(/__userName__/, userName);
bodyHtml = bodyHtml.replace(/__quantity__/, (numberOfNewStories==1)?"story":"stories");
if (!testStylesAdded) {
testStylesAdded = true;
var head = doc.getElementsByTagName("head")[0];
var newStyle = doc.createElement("style");
head.appendChild(newStyle);
var styleSheets = doc.styleSheets;
var styleSheet = styleSheets(styleSheets.length - 1);
styleSheet.cssText = getStringFromFunction(cssText);
newStyle = doc.createElement("link");
newStyle.type = "text/css";
newStyle.rel = "stylesheet";
newStyle.href = FACEBOOK_FEED_CSS_URL;
head.appendChild(newStyle);
styleSheets = doc.styleSheets;
styleSheet = styleSheets(styleSheets.length - 1);
}
testDiv.innerHTML = bodyHtml;
var testDivHeight = testDiv.clientHeight;
if (testDivHeight > maxNotificationHegiht)
var notificationHegiht = maxNotificationHegiht;
else
var notificationHegiht = testDivHeight;
var finalContentHeight = notificationHegiht - 68;
var html = getStringFromFunction(notificationHTML);
var css = getStringFromFunction(cssText);
css = css.replace(/__finalContentHeight__/, finalContentHeight);
html = html.replace(/__FACEBOOK_COMMON_CSS_URL__/, FACEBOOK_COMMON_CSS_URL);
html = html.replace(/__FACEBOOK_FEED_CSS_URL__/, FACEBOOK_FEED_CSS_URL);
html = html.replace(/__cssText__/, css);
html = html.replace(/__bodyHTML__/, bodyHtml);
html = html.replace(/__facebookURL__/g, facebookURL);
//log(html)
SSB.notify(html, 222, notificationHegiht, 15);
}
}
/*
* Removes:
* inside feed items
*