// ==UserScript==
// @name           friendfeedFilterByService
// @namespace      http://userscripts.org/scripts/show/47960
// @description    Shows only FriendFeed feed entries based on user chosen service type via either an inclusion (services to show) or exclusion (services to hide) list.
// @author         Micah Wittman
// @include        http://friendfeed.com/*
// @version        0.25
// ==/UserScript==

/*    

    Author: Micah Wittman | http://wittman.org/ | http://friendfeed.com/micahwittman
   
    Versions:
        * 2009-05-08 - version 0.25 - Added hideAtReplies feature which specifically filters out posts if the first character is an @ symbol. By default, the service must be Twitter, but there is also a companion option to hide @-replies from any service (tweets that route through facebook, etc).
        * 2009-05-07 - version 0.24 - Added feature which filters out by words in the name/text of post. If a word/phrase in a user-defined array (adjust withWordsToHideArr in Configuration) is found in the name/text of a post (just the post name/text, comments are not checked), the post will be hidden. To activate hiding by words, set withWordsToHideActive =  true.
        * 2009-05-05 - version 0.23 - Fixed endless loop page reload on non- feed pages (like the FF index page, account page, etc). Added autoPageReloadOnEmpty = true/false (in Configuration) which controls if the page auto-reloads when all visible entries drop off page (auto-reload only applicable when feedUpdateDelay = -1 ("indefinite" mode).
        * 2009-05-05 - version 0.22 - Added -1 as a valid feedUpdateDelay value (in Configuration) which is how to set the feed update delay to "indefinite".
                       Also, in "indefinite" mode, feed entries will drawn down to zero left at which point the page is automatically reloaded.
        * 2009-05-04 - version 0.21 - Fixed error: "everyoneEntries undefined". Fixed improves performance too.
        * 2009-05-04 - version 0.2 - Feed entries were not updating on all feed display types (/public was Ok, but a
                       home feed, for example, works differently behind the scenes and wasn't updating. Fixed.
        * 2009-05-03 - version 0.113 - Moved the feed update delay value (default is feedUpdateDelay = 500 milliseconds) into Configuration;
                       now it's easy to set how frequently you want the realtime updated. Effectively, this provides an inbetween option:
                       not paused, but not full throttle realtime (note that the flow of the feed actually continues, it's just revealed
                       in batches).
        * 2009-05-02 - version 0.112 - loosened the embed detect so querystring variable can be in any order
        * 2009-05-02 - version 0.111 - minor css style tweak
        * 2009-05-02 - version 0.11 - Embed styles added for a compact layout when using ?embed=1 querystring
        * 2009-05-01 - version 0.1 - initial release
*/

/************                **********/
/************  Configuration **********/
/************                **********/

//The filtering method is either in SHOW or HIDE mode (they are not functional at the same time)

var filterMethod = "NONE"; //Set to "SHOW" or "HIDE", or "NONE" (NONE turns off filtering, so everything is shown; but feedUpdateDelay is still in effect - set value below) then set the appropriate servicesToShowArr or servicesToHideArr variables below.

//The service names are defined by the service source link text (ie. for the example "3 minutes ago from Google Reader", the service name is Google Reader).
//Just a handful of examples are placed in the arrays below. Reference any service supported by FriendFeed.
//FriendFeed native posts are special in that there is no service name displayed, but the name "FriendFeed" used in this script's config arrays below will work to show are hide FriendFeed posts.

var servicesToShowArr = ["FriendFeed", "Twitter", "Tumblr", "Google Reader", "Facebook", "Bookmarklet"]; //Only service names in servicesToShowArr array will be shown (in other words, remove any service from the array you wanted filtered out)
var servicesToHideArr = ["Twitter", "Some other service"]; //Only service names in servicesToHideArr array will be hidden (in other words, every entry type will be shown except those in this array)

var withWordsToHideArr = ["Meme", "NSFW", "Social Media", "Sobert Rcoble"]; //Any Words/phrases in this array which is found in the name/text of a post will be cause the post to be hidden). Not case sensitive. Symbols are ignored (however, use var hideAtReplies below for hiding @-replies).
var withWordsToHideActive = false; //Set to true for hiding posts according to the withWordsToHideArr array above. Set to false to deactivate hiding by words.

var hideAtReplies = false; //Set to true to hide @-replies (any post, from Twitter or otherwise, that begins with @). Set to false to not use the @-reply hiding feature.
var hideAtRepliesFromAnyService = false; //Set to true to have @-reply hiding feature (var hideAtReplies above) hide from any service (hides from Twitter only by default). Set to false to only hide on feed entries identified specfically as "Twitter". ONLY WORKS IN CONJUNCTION WITH hideAtReplies = true.

var feedUpdateDelay = 500; //Number of milliseconds by which the realtime feed update is delayed (integer). Set to -1 (negative 1) for an indefinite delay. Note that feed entries will still drop off the page at the bottom at the normal rate, so an indefinite delay means the page will drawn down to blank until you perform a browser refresh.

var autoPageReloadOnEmpty = true; //Set true to have page automatically reload when last visible entry drops off, and set to false to not reload when entries draw down to zero. Note: Auto reload only applies when feedUpdateDelay is -1 ("indefinite" mode).

var style = '';
//style - Auto-detect Embed window (via querystring)
if( window.location.href.indexOf('embed=1') > -1 ){
    style = 'EMBED';
}
//style = 'EMBED'; // Manual-override: Uncomment this line of code and Set to '' for default (no change to layout). 'EMBED' for no sidebar and other peripheral elements display.

/**************************************/
/**************************************/


var realtime;
var lastSuccessPrev;

var prevEveryoneEntriesSerialized = null;

var everyoneEntries = null;

function GM_wait()
{
    if(typeof unsafeWindow != 'undefined')
    {
        if(typeof unsafeWindow.everyoneEntries != 'undefined'){
            everyoneEntries = unsafeWindow.everyoneEntries;
        }
        if(typeof unsafeWindow.jQuery != 'undefined')
        {
            jQuery = unsafeWindow.jQuery;
        }
        if(typeof unsafeWindow.realtime != 'undefined'){
            realtime = unsafeWindow.realtime;
        }
    }
    if(typeof jQuery == 'undefined')
    {
        window.setTimeout(GM_wait,251);
    }
    else
    {
        $ = jQuery;
        initConfig();
        AddIndicator();
        AddHeadStyle();
        letsJQuery();
    }

}
GM_wait();

function initConfig()
{
}

function FilterByService_wait(){
    var feedUpdateDelayToUse = feedUpdateDelay;
    if(feedUpdateDelay < 0)
    {
        feedUpdateDelayToUse = 2000;
        if(autoPageReloadOnEmpty)
        {
            checkPageReload();
        }
    }
    if(everyoneEntries != null)
    { //Public feed type update detected
        if(everyoneEntries.toSource() != prevEveryoneEntriesSerialized)
        {
            prevEveryoneEntriesSerialized = everyoneEntries.toSource();
            if(feedUpdateDelay >= 0){
                letsJQuery();
            }
            else
            {
                window.setTimeout(FilterByService_wait, feedUpdateDelayToUse);
            }
        }
        else
        {
            window.setTimeout(FilterByService_wait, feedUpdateDelayToUse);
        }
    }
    else
    { //Home feed type update detected
        if(lastSuccessPrev != realtime.lastSuccess)
        {
            lastSuccessPrev = realtime.lastSuccess;
            if(feedUpdateDelay >= 0){
                letsJQuery();
            }
            else
            {
                window.setTimeout(FilterByService_wait, feedUpdateDelayToUse);
            }
        }
        else
        {
            window.setTimeout(FilterByService_wait, feedUpdateDelayToUse);
        }
    }
}

function isReloadablePage()
{
    if($('div#feed').length == 0)
    {
        return false;
    }
    if(
        isSearchPage()
        || isIndexPage()
    )
    {
        return false;
    }
    else
    {
        return true;
    }
}

function isIndexPage()
{
    if(window.location.href.replace('https:','http:') == 'http://friendfeed.com/'){return true;}else{return false;}
}

function isSearchPage()
{
    if(window.location.href.indexOf('/search') > -1){return true;}else{return false;}
}

function checkPageReload(){
    var reloadOk = true;
    var counter = 0;
    var info = $('.entry').each(function(){
        if( $(this).css('display') == 'block' )
        {
            counter++;
        }
    });
   
    if(!isReloadablePage())
    {
        reloadOk = false;
    }   
    //console.log(counter);
    if(counter == 0)
    {
        if(reloadOk)
        {
            window.location.reload();
        }
    }   
}

function AddIndicator(){
    filterMethod = filterMethod.toUpperCase();
    var labelOpen = '(';
    var labelClose = ')';
    var show_hide;
    var show_hideFull;
    var dots = '';
    var max = 40;
	var hideByWords = '';
	var hideByWordsFull = '';
	if(withWordsToHideActive)
	{
		max = 22;
		hideByWords = ' | HIDE Words';
		hideByWordsFull += '\r\n\r\n' + 'Hide by Words: ' + withWordsToHideArr.join(', ');
	}
	if(hideAtReplies)
	{
		var hideAtRepliesFromAnyServiceDsp = ' (Twitter)';
		if(hideAtRepliesFromAnyService)
		{
			hideAtRepliesFromAnyServiceDsp = ' (any service)';
		}
		hideByWords += ', @-replies';
		hideByWordsFull += '\r\n\r\n' + 'Hide @-replies' + hideAtRepliesFromAnyServiceDsp;
	}
    if(filterMethod == 'NONE'){
        labelOpen = 'DELAY: ';
        labelClose = '';
        if(feedUpdateDelay >= 0)
        {
            show_hide = (feedUpdateDelay / 1000) + ' seconds';
        }
        else
        {
            show_hide = 'Indefinite';
        }
        show_hideFull = 'Feed Update Delay: ' + show_hide;
    }else if(filterMethod == 'SHOW'){
        var show = servicesToShowArr.join(', ');
        if(show.length > max){
            dots = '...';
        }
        show_hide = 'SHOW: ' + show.substr(0,max) + dots;
        show_hideFull = 'SHOW: ' + show;
    }else{
        var hide = servicesToHideArr.join(', ');
        if(hide.length > max){
            dots = '....';
        }
        show_hide = 'HIDE: ' + hide.substr(0,max) + dots;
        show_hideFull = 'HIDE Services: ' + hide;
    }
    var compactMasthead = '';
    var switchCss = 'margin-left:6px;';
    if(style == 'EMBED'){
        compactMasthead = '<a href="http://friendfeed.com/">FRIEND FEED</a> :: ';
        switchCss = ''; //don't indent for embed view
    }
	show_hide += hideByWords;
    $('#logo:first').append('<div style="padding-top:8px;padding-bottom:8px;font-size:10px;' + switchCss + '">' + compactMasthead + '<a id="localFilterLink" href="#">' + labelOpen + show_hide + labelClose + '</a></div>');
    $('#localFilterLink').click(function(){
        alert('Your friendfeedFilterByService greasemonkey script is turned "ON". There are likely filtered entries you are not currently seeing.\r\n\r\nCurrent Configuration is...\r\n\r\n' + show_hideFull + hideByWordsFull);
    });
}

function AddHeadStyle(){
    var optionalCss = '';
    if(style == 'EMBED'){
        optionalCss = ' #sidebar,.home,.sharebox,#footer,#search,.bar,#logo img{ display:none; }  '
            + ' #page .main{ padding:0; }'
            + ' #header{ margin:0 0 0 0; }'
            + ' body{ background:none !important; background-color:#fff !important; }'
            + ' #body{ max-width:3000px !important; }'
            + ' .bar{ border:none !important; background:none !important;background-color:#C8DCF5 !important; }'
            ;
    }
    var css = "@namespace url(http://www.w3.org/1999/xhtml); .entry{ display:none; }" + optionalCss;
    if (typeof GM_addStyle != "undefined") {
        GM_addStyle(css);
    } else if (typeof addStyle != "undefined") {
        addStyle(css);
    } else {
        var heads = document.getElementsByTagName("head");
        if (heads.length > 0) {
            var node = document.createElement("style");
            node.type = "text/css";
            node.appendChild(document.createTextNode(css));
            heads[0].appendChild(node);
        }
    }
}

function letsJQuery()
{   
    FilterByService();   
    FilterByService_wait();
}

function isRemoveFF(){
    var doRemove = false;
    if(filterMethod ==  'SHOW'){
        if( $.inArray("FriendFeed", servicesToShowArr) == -1 ){ //FriendFeed entries don't have a service <a> tag, so don't remove if FF entry type is to be shown
            doRemove = true;
            //console.log($.inArray("FriendFeed", servicesToShowArr));
        }
    }else{ //HIDE
        if( $.inArray("FriendFeed", servicesToHideArr) > -1 ){
            doRemove = true;
            //console.log($.inArray("FriendFeed", servicesToShowArr));
        }
    }
    return doRemove;
}

function FilterByWordMatched(text, serviceName){
	var word;
    var re;
	var matched = false;
	var matchedIndex;
	$.each(withWordsToHideArr, function(){
	    word = this;
	    re = new RegExp('^' + word +'|([^a-z]' + word + '[^a-z])|([^a-z]' + word + '$)', 'i');
		//console.log(word);
		matchedIndex = text.match(re);
	    if(matchedIndex != null)
	    {
			matched = true;
	    }
	});
	if(!matched)
	{
		var serviceMatch = true;
		if( (!hideAtRepliesFromAnyService) && (serviceName != 'Twitter') )
		{
			serviceMatch = false;
		}
		if(hideAtReplies && serviceMatch)
		{
			if(text.indexOf('@') == 0)
			{
	    		matched = true;
			}
		}
	}
	return matched;
}

function FilterByService(){   
    var info = $('.entry .body .info');
    info.each(function(){
        var service = $(this).find('a').eq(1);
        var doRemove = false;
        var entry = service.parent().parent().parent();
		var serviceName = $(this).find('.service').text();
        var entryText = service.parent().parent().find('.text');
		var entryNameText = service.parent().parent().find('a.l_profile:first');
        if(filterMethod != 'NONE'){
            if(service.attr('className') == 'service'){
                //Has service tag
                if(filterMethod ==  'SHOW'){
                    if( $.inArray(service.text(), servicesToShowArr) == -1 ){
                        doRemove = true;
                    }
                }else{ //HIDE
                    if( $.inArray(service.text(), servicesToHideArr) > -1 ){
                        doRemove = true;
                    }
                }
            }else{
                //Does not have service tag
                doRemove = isRemoveFF();
            }
        }
        //console.log(entryText.text());
        if(withWordsToHideActive)
        {
        	var entryTextLower = entryText.text().toLowerCase();
			var entryNameTextLower = entryNameText.text().toLowerCase();
			doRemove = FilterByWordMatched(entryTextLower + ' ' + entryNameTextLower, serviceName);
        }
        if(doRemove){
            entry.remove();
        }else{
            entry.css('display','block');
        }
    });
}


if(typeof unsafeWindow != 'undefined')
{
    // ========= ADD FROM HERE ONWARDS TO YOUR SCRIPT =========
    // This auto update-notification script was made by Seifer
    // You can find it at http://userscripts.org/scripts/show/12193
    // ========================================================
    // === Edit the next four lines to suit your script. ===
    scriptName='friendfeedFilterByService';
    scriptId='47960';
    scriptVersion=0.25;
    scriptUpdateText=">>REMINDER: Backup your Configuration section (for reference) before updating<< [v0.25]: Added hideAtReplies feature which specifically filters out posts if the first character is an @ symbol. By default, the service must be Twitter, but there is also a companion option to hide @-replies from any service (tweets that route through facebook, etc). [v0.24]: Added a feature which filters out by words in the name/text of post. If a word/phrase in a user-defined array (adjust withWordsToHideArr in Configuration) is found in the name/text of a post (just the post name/text, comments are not checked), the post will be hidden. To activate hiding by words, set withWordsToHideActive =  true.";
    // === Stop editing here. ===

    var lastCheck = GM_getValue('lastCheck', 0);
    var lastVersion = GM_getValue('lastVersion', 0);
    var d = new Date();
    var currentTime = Math.round(d.getTime() / 1000); // Unix time in seconds
    if (parseInt(navigator.appVersion)>3) {
        if (navigator.appName=="Netscape") {
            winW = window.innerWidth;
            winH = window.innerHeight;
        }
        if (navigator.appName.indexOf("Microsoft")!=-1) {
            winW = document.body.offsetWidth;
            winH = document.body.offsetHeight;
        }
    }
    if (currentTime > (lastCheck + 86400)) { //24 hours after last check
        GM_xmlhttpRequest({
            method: 'GET',
            url: 'http://userscripts.org/scripts/review/'+scriptId+'?format=txt',
            headers: {'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey','Accept': 'text/plain',},
            onload: function(responseDetails) {
                var text = responseDetails.responseText;
                    var onSiteVersion = text.substring(text.indexOf("scriptVersion=")+14,text.indexOf("\n",text.indexOf("scriptVersion="))-2);
                    var onSiteUpdateText = text.substring(text.indexOf("scriptUpdateText=")+18,text.indexOf("\n",text.indexOf("scriptUpdateText="))-3);
                    if(onSiteVersion > scriptVersion && onSiteVersion > lastVersion) {
                        GM_addStyle('#gm_update_alert {'
                    +'    position: fixed;'
                    +'    z-index:100000;'
                    +'    top: '+((winH/2)-60)+'px;'
                    +'    left: '+((winW/2)-275)+'px;'
                    +'    width: 550px;'
                    +'    background-color: yellow;'
                    +'    text-align: center;'
                    +'    font-size: 11px;'
                    +'    font-family: Tahoma;'
                    +'}'
                    +'#gm_update_alert_buttons {'
                    +'    position: relative;'
                    +'    top: -5px;'
                    +'    margin: 7px;'
                    +'}'
                    +'#gm_update_alert_button_close {'
                    +'    position: absolute;'
                    +'    right: 0px;'
                    +'    top: 0px;'
                    +'    padding: 3px 5px 3px 5px;'
                    +'    border-style: outset;'
                    +'    border-width: thin;'
                    +'    z-index: inherit;'
                    +'    background-color: #FF0000;'
                    +'    color: #FFFFFF;'
                    +'    cursor:pointer'
                    +'}'
                    +'#gm_update_alert_buttons span, #gm_update_alert_buttons span a  {'
                    +'    text-decoration:underline;'
                    +'    color: #003399;'
                    +'    font-weight: bold;'
                    +'    cursor:pointer'
                    +'}'
                    +'#gm_update_alert_buttons span a:hover  {'
                    +'    text-decoration:underline;'
                    +'    color: #990033;'
                    +'    font-weight: bold;'
                    +'    cursor:pointer'
                    +'}');
                        newversion = document.createElement("div");
                        newversion.setAttribute('id', 'gm_update_alert');
                        newversion.innerHTML = ''
                    +'    <b>GreaseMonkey UserScript Update Notification</b><br>'
                    +'    There is an update available for &quot;'+scriptName+'&quot; <br>'
                    +'    You are currently running version '+scriptVersion+'. The newest version is '+onSiteVersion+'.<br>'
                    +'    <br>'
                    +'    <div id="gm_update_alert_button_close">'
                    +'        Close</div>'
                    +'    <b>What do you want to do?</b><br>'
                    +'    <div id="gm_update_alert_buttons">'
                    +'        <span id="gm_update_alert_button_showinfo"><a href="#">Show&nbsp;Update&nbsp;Info</a></span>&nbsp;&nbsp;'
                    +'        <span id="gm_update_alert_button_scripthome"><a target="_blank" href="http://userscripts.org/scripts/show/'+scriptId+'">Go&nbsp;To&nbsp;Script&nbsp;Homepage</a></span>&nbsp;&nbsp;'
                    +'        <span id="gm_update_alert_button_upgrade"><a href="http://userscripts.org/scripts/source/'+scriptId+'.user.js">Upgrade&nbsp;to&nbsp;version&nbsp;'+onSiteVersion+'</a></span>&nbsp;&nbsp;'
                    +'        <span id="gm_update_alert_button_wait"><a href="#">Don&#39;t&nbsp;remind&nbsp;me&nbsp;again&nbsp;until&nbsp;tomorrow</a></span>&nbsp;&nbsp;'
                    +'        <span id="gm_update_alert_button_waitnextversion"><a href="#">Don&#39;t&nbsp;remind&nbsp;me&nbsp;again&nbsp;until&nbsp;the&nbsp;next&nbsp;new&nbsp;version</a></span> </div>';
                    document.body.insertBefore(newversion, document.body.firstChild);
                    document.getElementById('gm_update_alert_button_showinfo').addEventListener('click', function(event) {alert(onSiteUpdateText);}, true);
                    document.getElementById('gm_update_alert_button_wait').addEventListener('click', function(event) {GM_setValue('lastCheck', currentTime);alert("You will not be reminded again until tomorrow.");document.body.removeChild(document.getElementById('gm_update_alert'));}, true);
                              document.getElementById('gm_update_alert_button_waitnextversion').addEventListener('click', function(event) {GM_setValue('lastVersion', onSiteVersion);alert("You will not be reminded again until the next new version is released.");document.body.removeChild(document.getElementById('gm_update_alert'));}, true);
                    document.getElementById('gm_update_alert_button_close').addEventListener('click', function(event) {document.body.removeChild(document.getElementById('gm_update_alert'));}, true);
                    }
                }
        });
    }
}

