Friend Feed Twitter Client

By engtech Last update Sep 3, 2008 — Installed 3,167 times.

There are 1 previous version of this script.

/*
--------------------------------------------------------------------
  How to Install a Greasemonkey Script
--------------------------------------------------------------------

- You need the Firefox web browser - Download and install
  http://www.mozilla.com/en-US/firefox/

- You need to install Greasemonkey - How-To
  http://internetducttape.com/2007/08/23/howto-install-firefox-extensions-screenshots/

- Install this script - How-To
  http://internetducttape.com/2007/08/24/howto-use-firefox-greasemonkey-userscripts-screenshots/

*/

// ==UserScript==
// @name           Friend Feed Twitter Client
// @namespace      http://internetducttape.com
// @description    Adds a character counter to the comment field, clicks the 'reply on Twitter' and has an interface for sending Tweets
// @include        http://friendfeed.com/*
// @exclude        http://friendfeed.com/settings*
// @version        0.44
// ==/UserScript==


var friendfeedtwitter_version = "0.44";

(function() {
  var twit_ui = null;
  var user = null;
  var pass = null;
  var update_url = null;

  autoUpdateFromUserscriptsDotOrg({
    name: 'Friend Feed Twitter Client',
	url: 'http://userscripts.org/scripts/source/24396.user.js',
	version: friendfeedtwitter_version,
	});

  //  GM_addStyle('#idt-twitter .idt-promote { visibility:hidden; } #idt-twitter:hover .idt-promote { visibility:visible; }');

  GM_registerMenuCommand("Friend Feed Twitter Client: Set keyboard shortcut", setKeyboardShortcut);

  function setKeyboardShortcut() {
    var hotkey = prompt("Please enter keyboard shortcut. Examples:\nALT+C\nALT+SHIFT+C\nCTRL+ALT+SHIFT+Z");
    GM_setValue("SHORTCUT", hotkey);
  }

  function xhrTweet(tweet) {
    GM_xmlhttpRequest({url:update_url, 
			  method:"POST",
			  headers:{
			  "Content-Type": "application/x-www-form-urlencoded",
			    "X-Twitter-Client":"twitter.user.js",
			    "X-Twitter-Client-Version":friendfeedtwitter_version,
			    "X-Twitter-Client-URL":"http://internetducttape.com"},
			  data:"source=idt&status="+encodeURIComponent(tweet),
			  onload:function(xhr){
			  $('#idt-twitter').hide();
			},
			  onerror:function(xhr){
			  alert("Unable to send Tweet. Bad username/password? Is Twitter down?");
			}
		      });
  }

  // Have to use a document event listener to be able to use GM XHR because of unsafeWindow.
  // Need GM XHR to do cross-site XHR to Twitter.
  function sendTweet(ev) {
    if (ev.target.getAttribute("idt")) {
      var tweet = $('#idt-twitter textarea').val();
      if (tweet.length > 0) {
	xhrTweet(tweet);
      }
    }
  }

  function twitter_build_ui() {
    if (twit_ui) {
      twit_ui.find('textarea').val('');
      //	  twit_ui.find('idt-character-count').val('140');
      twit_ui.show();
      twit_ui.find('textarea').focus();
    }
    else {
      $('#body').before('<div id="idt-twitter" style="padding-left: 35px; padding-top: 5px; display:block;"><table style="display:block;"><tr><td colspan="3"><textarea class="" name="comment" style="color: gray; width: 35em; margin-bottom: -3px; height: 6em; line-height: 1em;"/></td></tr><tr style="line-height: 2em;"><td><input idt="hack" id="idt-tweet" type="submit" value="Tweet" style="margin-left: 3px;"/><input id="idt-hide" type="submit" value="Hide" style="margin-left: 3px;"/></td><td align="left"><a class="idt-promote" title="Click to share Twitter Client on Friend Feed script with your friends" target="_blank" href="http://friendfeed.com/share?url=http://internetducttape.com/2008/03/27/greasemonkey-scripts-friend-feed-twitter-client-and-remove-visited-links/&title=How%20to%20use%20Twitter%20from%20Friend%20Feed">Share this script</a></td><td align="right">Characters left: <strong id="idt-character-count">140</strong></td></tr></table></div>');

      twit_ui = $('#idt-twitter');
      twit_ui.find('#idt-hide').click(function() {
					$('#idt-twitter').hide();
				      });
      document.addEventListener('click', sendTweet, false);
      twit_ui.find('textarea').keyup(function() {
				       var cc = $('#idt-character-count');
				       cc.html(140 - this.value.length);
				       // Twitter colors
				       if (this.value.length > 130) {
					 cc.css('color','#d40d12');
				       } else if (this.value.length > 120) {
					 cc.css('color','#5c0002');
				       } else {
					 cc.css('color','#cccccc');
				       }			 
				     });

    }
  }

  function shortcut(shortcut,callback,opt) {
    // from: Aaron Bassett http://foobr.co.uk and http://www.openjs.com/scripts/events/keyboard_shortcuts/

    var default_options = {'type':'keydown','propagate':false,'target':document}
    if(!opt) opt = default_options;
    else {for(var dfo in default_options) {if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];}}
    var ele = opt.target;
    if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
    var ths = this;
    var func = function(e) {
      e = e || window.event;
      if (e.keyCode) code = e.keyCode;
      else if (e.which) code = e.which;
      var character = String.fromCharCode(code).toLowerCase();
      var keys = shortcut.toLowerCase().split("+");
      var kp = 0;
      var shift_nums = {"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":"\"",",":"<",".":">","/":"?","\\":"|"}
      var special_keys = {'esc':27,'escape':27,'tab':9,'space':32,'return':13,'enter':13,'backspace':8,'scrolllock':145,'scroll_lock':145,'scroll':145,'capslock':20,'caps_lock':20,'caps':20,'numlock':144,'num_lock':144,'num':144,'pause':19,'break':19,'insert':45,'home':36,'delete':46,'end':35,'pageup':33,'page_up':33,'pu':33,'pagedown':34,'page_down':34,'pd':34,'left':37,'up':38,'right':39,'down':40,'f1':112,'f2':113,'f3':114,'f4':115,'f5':116,'f6':117,'f7':118,'f8':119,'f9':120,'f10':121,'f11':122,'f12':123}
      for(var i=0; k=keys[i],i<keys.length; i++) {
	if(k == 'ctrl' || k == 'control') {if(e.ctrlKey) kp++;
	} else if(k ==  'shift') {if(e.shiftKey) kp++;
	} else if(k == 'alt') {if(e.altKey) kp++;
	} else if(k.length > 1) { if(special_keys[k] == code) kp++;
	} else { if(character == k) kp++;
	  else {if(shift_nums[character] && e.shiftKey) { 
	      character = shift_nums[character]; 
	      if(character == k) kp++;
	    }
	  }
	}
      }
      if(kp == keys.length) {
	callback(e);
	if(!opt['propagate']) {
	  e.cancelBubble = true;
	  e.returnValue = false;
	  if (e.stopPropagation) {
	    e.stopPropagation();
	    e.preventDefault();
	  }
	  return false;
	}
      }
    }
    if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
    else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
    else ele['on'+opt['type']] = func;
  }


  function setup_twitter(force) {
    user = GM_getValue("TWITTER_USERNAME", null);
    pass = GM_getValue("TWITTER_PASSWORD", null);
    var test_twitter = false;
    // Ask the user for credentials
    if ((null == pass) || (null == user) || force) {	  
      // FF caches the twitter username.
      if (unsafeWindow.gTwitLogin) {
	if (window.confirm('User twitter username '+unsafeWindow.gTwitLogin+'?')) {
	  user = unsafeWindow.gTwitLogin;
	}
	else {
	  user = window.prompt('Enter your Twitter username');
	}
	pass = window.prompt('Enter your Twitter password');
      }
      GM_setValue("TWITTER_USERNAME", user);
      GM_setValue("TWITTER_PASSWORD", pass);
      test_twitter = true;
    }
    update_url = ["http://", user, ":", pass, "@",
		  "twitter.com"].join("") + "/statuses/update.json";
    // Validate that the user entered valid credentials and do some shameless self promotion.
    if (test_twitter) {
      xhrTweet("Trying Friend Feed Twitter Client from http://InternetDuctTape.com");
    }
    var hotkey = GM_getValue("SHORTCUT", 'CTRL+ALT+SHIFT+Z');
    // See: http://jshotkeys.googlepages.com/test-static.html
    shortcut(hotkey, twitter_build_ui );
  };

  if ('function' != typeof(unsafeWindow.origSetTweetBackHtml)) {
    unsafeWindow.origSetTweetBackHtml = unsafeWindow.setTweetBackHtml;
    unsafeWindow.setTweetBackHtml = function(B) {
      unsafeWindow.origSetTweetBackHtml(true);
    };
  }

  // Uses jQuery
  function lets_jquery() {   
    // Add Twitter box after the settings menu
    $('.settings').find('.l_tab').after('<td class="l_tab" style="padding-left: 10px;"><div class="rounded bb"><div class="t"><div class="l"><div class="r"><div class="tl"><div class="tr"><div class="body"><a id="idt-tweet-link" href="#">tweet</a></div></div></div></div></div></div></div></td>');
    $('#idt-tweet-link').click(function() {
				 twitter_build_ui();
			       });
  }

  function jquery_wait() {
    if(typeof unsafeWindow.jQuery == 'undefined') { window.setTimeout(jquery_wait,100); }
    else { $ = unsafeWindow.jQuery; lets_jquery(); }
  }

  makeMenuFunction("Reset twitter username/password", function(){ setup_twitter(true); }, "Friend Feed Twitter Client");

  jquery_wait();
  setup_twitter(false);

 })();

function makeMenuFunction(desc, fn, prefix) {
  // The first argument is the description for the command
  // The second argument is the function to call
  // The third option is an option prefix for this menu item
  GM_registerMenuCommand((prefix ? prefix+": " : "") + desc, fn);
}

function autoUpdateFromUserscriptsDotOrg(SCRIPT) {
  // Update code from Junk Blocker: http://loonyone.livejournal.com/
  // usage example
  // autoUpdateFromUserscriptsDotOrg({
  //   name: 'RSS+Atom Feed Subscribe Button Generator',
  //   url: 'http://userscripts.org/scripts/source/688.user.js',
  //   version: "1.2",
  // });
  try {
    if (!GM_getValue) return; // Older version of Greasemonkey. Can't run.

    // avoid a flood of dialogs e.g. when opening a browser with multiple tabs set to homepage
    // and a script with * includes or opening a tabgrop
    var DoS_PREVENTION_TIME = 2 * 60 * 1000;
    var isSomeoneChecking = GM_getValue('CHECKING', null);
    var now = new Date().getTime();
    GM_setValue('CHECKING', now.toString());

    if (isSomeoneChecking && (now - isSomeoneChecking) < DoS_PREVENTION_TIME) return;

    // check daily
    var ONE_DAY = 24 * 60 * 60 * 1000;
    var ONE_WEEK = 7 * ONE_DAY;
    var TWO_WEEKS = 2 * ONE_WEEK;
    var lastChecked = GM_getValue('LAST_CHECKED', null);
    if (lastChecked && (now - lastChecked) < TWO_WEEKS) return;

    GM_xmlhttpRequest({
      method: 'GET',
	  url: SCRIPT.url + '?source', // don't increase the 'installed' count just for update checks
	  onload: function(result) {
	  if (!result.responseText.match(/@version\s+([\d.]+)/)) return;     // did not find a suitable version header

	  var theOtherVersion = parseFloat(RegExp.$1);
	  if (theOtherVersion <= parseFloat(SCRIPT.version)) return;      // no updates or older version on userscripts.orge site

	  if (window.confirm('A new version ' + theOtherVersion + ' of greasemonkey script "' + SCRIPT.name + '" is available.\nYour installed version is ' + SCRIPT.version + ' .\n\nUpdate now?\n')) {
	    GM_openInTab(SCRIPT.url);   // better than location.replace as doing so might lose unsaved data
	  }
	}
      });
    GM_setValue('LAST_CHECKED', now.toString());
  } catch (ex) {
  }
}