Tweetpreview

By Chilla42o Last update Aug 22, 2009 — Installed 224 times. Daily Installs: 0, 2, 0, 0, 0, 1, 4, 0, 4, 0, 1, 4, 1, 0, 0, 4, 0, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 3, 1, 0, 0

There are 18 previous versions of this script.

// ==UserScript==
// @name           Tweetpreview
// @namespace      Chilla42o
// @description    Tweetpreview shows you a live-preview of what you are going to post on Twitter.
// @version        0.4.2
// @include        http://twitter.com/*
// ==/UserScript==

tweetpreview = {

  status: null,
  currentpage: null,
  profile: {},
  statustext: '',
  pageswitched: true,
  replyto:null,
  replytostatusid:null,
  
  initialize: function() {
    if (typeof unsafeWindow.jQuery == 'undefined') { 
       window.setTimeout(tweetpreview.initialize, 120); 
       return false; 
    }

    $ = unsafeWindow.jQuery;
    jQuery = $;
    twttr = unsafeWindow.twttr;

    tweetpreview.currentpage = $('body').attr('id');
    if (!tweetpreview.currentpage.match(/(home|replies|favorites|search|direct_messages|inbox|sent|create)/)) {
      return false;
    }

    tweetpreview.addstyle([
      'textarea#status, textarea#text { height:3.5em; }',
      'body#direct_messages div#tweetpreview span.source,',
      'body#inbox div#tweetpreview span.source,',
      'body#sent div#tweetpreview span.source,',
      'body#create div#tweetpreview span.source,',
      'div#tweetpreview.direct-message span.source,',
      'body#direct_messages div#tweetpreview a.inreplyto,',
      'body#inbox div#tweetpreview a.inreplyto,',
      'body#sent div#tweetpreview a.inreplyto, ',
      'body#create div#tweetpreview a.inreplyto, ',
      'div#tweetpreview.direct-message a.inreplyto { display:none }',
      'body#create div#tweetpreview { margin-left:105px; }',
      'div#tweetpreview { display:none; border:1px dashed #d2dada; border-style:dashed none; font-size:1.2em; line-height:1.1em; margin:10px; width:518px; padding:10px 0 10px 5px;  }',
      'div#tweetpreview:hover { background:#fafafa; }',
      'div#tweetpreview * { margin:0; padding:0; }',
      'div#tweetpreview > div { display:inline-block; vertical-align:top; margin:0; padding:0; }',
      'div#tweetpreview div.avatar { width:48px; margin-right:8px; }',
      'div#tweetpreview div.tweet { width: 420px; margin-right:4px; }',
      'div#tweetpreview div.tweet p { margin:0; padding:0; }',
      'div#tweetpreview div.tweet p.info { margin-top:3px; color:#999; font-size:0.764em; }',
      'div#tweetpreview div.tweet p.info a { color:#999; cursor:pointer; }',
      'div#tweetpreview div.tweet p.info a.reply { display:none; }',
      'div#tweetpreview div.tweet p.message { margin:0; padding:0;  }',
      'div#tweetpreview div.actions { width:20px; }',
      'div#tweetpreview div.actions a { display:none; width:24px; height:24px; background: url(http://s.twimg.com/a/1250203207/images/icon_reply.gif) no-repeat center; }',
      'div#tweetpreview div.actions a.favorite { background-image:url(http://s.twimg.com/a/1250203207/images/icon_star_empty.gif); }',
      'div#tweetpreview:hover div.actions a { display:block; }',
    ]);

    /* detect page switch */
    $('body').bind('DOMAttrModified', function(e) {
      if (e.target.tagName == 'BODY'&& e.attrName == 'id') {
        tweetpreview.pageswitch(e.prevValue, e.newValue);
        tweetpreview.pageswitched = true;
      } else if (e.target.tagName == 'LI' && 
                e.attrName == 'class' && 
                e.target.id.slice(-4) == '_tab' && 
                e.prevValue.indexOf('loading') > -1 && 
                e.newValue.indexOf('loading') == -1) 
      {  
        if (!tweetpreview.pageswitched) {
          tweetpreview.pagerefresh(e.target.id.slice(0,-4));  
        }
        tweetpreview.pageswitched = false;
      } 
    });

    if (tweetpreview.currentpage != 'create') {

      tweetpreview.profile = {
        username: $('span#me_name').html(),
        name: $('span#me_name').parent('a').attr('title'),
        image: $('div#profile img:first').attr('src'),
        url: $('span#me_name').parent('a').attr('href'),
        linkcolor: $('a#home_link').css('color')
      }

      $('ol#timeline span.actions a.reply').livequery('click', function() {
        if (!tweetpreview.currentpage.match(/(home|replies|favorites|search)/)) return false;
        if (reply = $(this).attr('href').match(/in_reply_to_status_id=([0-9]+)\&in_reply_to=([A-Za-z0-9_]{1,15})/)) {
          tweetpreview.replytostatusid = reply[1];
          tweetpreview.replyto = reply[2];
          tweetpreview.statustext = tweetpreview.util.encodehtml($('textarea#status').val());
          tweetpreview.update();
        } 
      });

      tweetpreview.createpreview();

    } else {

      tweetpreview.profile = {
        username: $('meta[name=session-user-screen_name]').attr('content'),
        url: 'http://twitter.com/'+$('meta[name=session-user-screen_name]').attr('content'),
        linkcolor: $('a#home_link').css('color')
      }

      if (!twttr.form_authenticity_token) { 
        return false;
      }

      $.ajax({
        type: 'GET',
        url: 'http://twitter.com/status/user_timeline/'+tweetpreview.profile.username+'.json?count=1',
        dataType: 'json',
        data: {
          twttr: true,
          form_authenticity_token: twttr.form_authenticity_token
        },
        success: function(data) {
          var details = [];
          if (typeof data[0] == 'undefined') return false;
          var userinfo = data[0].user;
          tweetpreview.profile.image = userinfo.profile_image_url;
          tweetpreview.profile.name = userinfo.name;
          tweetpreview.createpreview();
        }
      });
    }

  },

  createpreview: function() {
    
    if (tweetpreview.currentpage != 'create') {
      var aftertarget = $('div#dm_update_box'); 
    } else {
      var aftertarget = $('form#direct_message_form');
    }

    aftertarget.after([
      '<div id="tweetpreview">',
        '<div class="avatar">',
          '<a title="'+tweetpreview.profile.name+'" href="'+tweetpreview.profile.url+'">',
            '<img src="'+tweetpreview.profile.image+'" alt="'+tweetpreview.profile.username+'" />',
          '</a>',
        '</div>',
        '<div class="tweet">',
          '<p class="message">',
             '<strong><a href="/'+tweetpreview.profile.username+'">'+tweetpreview.profile.username+'</a></strong>',
             '<span id="tweetpreview-message">',
             '</span>',
          '</p>',
          '<p class="info"><a>less than 0 seconds ago</a><span class="source"> from web</span> <a class="inreplyto"></a></p>',
        '</div>',
        '<div class="actions">',
          '<a href="http://userscripts.org/scripts/show/53554" class="favorite" title="Tweetpreview"></a>',
          '<a class="reply" title="reply to '+tweetpreview.profile.username+'"></a>',
        '</div>',
      '</div>'
    ].join("\n")); 
    
    $('textarea#status,textarea#text').bind('focus blur click keyup change', function() {
      tweetpreview.statustext = tweetpreview.util.encodehtml($(this).val());
      tweetpreview.update();
    });
   
    $('div#tweetpreview a[href]').livequery('click', function() { window.open($(this).attr('href')); return false; });
    tweetpreview.focustextarea();

  },

  focustextarea:function()  {
    if (tweetpreview.currentpage.match(/(inbox|outbox)/)) {
      $('textarea#text').focus();
    } else {
      $('textarea#status').focus();
    }
  },

  pageswitch:function(previouspage, currentpage) {
    tweetpreview.currentpage = currentpage;
    $('textarea#text,textarea#status').val(tweetpreview.statustext); 
    tweetpreview.focustextarea();
  },
  
  pagerefresh:function(currentpage) {
    tweetpreview.currentpage = currentpage;
    tweetpreview.focustextarea();
  },

  format_hashtag_links:function (statustext, space, hashtag) {
    return space+'<a class="hashtag" title="#'+hashtag+'" href="/search?q=%23'+escape(hashtag)+'">#'+hashtag+'</a>'
  },

  format_linktext:function(url) {
    if (url.length > 41) {
      return 'http://bit.ly/PrEV4';
    } else {
      return url.length > 27 ? url.substr(0, 27)+'...' : url;
    }
  },

  format_site_links:function(url) {
    return '<a href="'+url+'">'+tweetpreview.format_linktext(url)+'</a>';
  },
  
  format_cut_links:function(url) {
    tweetpreview.statuslinks.push(url);
    return '<a>'+(tweetpreview.statuslinks.length-1)+'</a>';
  },

  format_paste_links:function(linktag, linkindex) {
    return tweetpreview.statuslinks[linkindex];
  },
 
  update:function() {
    if (tweetpreview.statustext.length) {
      if (!$('div#tweetpreview').is(':visible')) {
         $('div#tweetpreview').fadeIn('fast');
      }
      if (tweetpreview.currentpage.match(/(home|replies|favorites|search)/)) {
        if (tweetpreview.replyto && tweetpreview.replytostatusid) {
          if (tweetpreview.statustext.indexOf('@'+tweetpreview.replyto) == -1) {
            $('div#tweetpreview a.inreplyto').removeAttr('href').empty().hide();
          } else {
            $('div#tweetpreview a.inreplyto').attr('href', 'http://twitter.com/'+tweetpreview.replyto+'/status/'+tweetpreview.replytostatusid)
                                              .html('in reply to '+tweetpreview.replyto)
                                              .show();
          }
        } else {
          $('div#tweetpreview a.inreplyto').removeAttr('href').empty().hide();
        }
      } else {
        $('div#tweetpreview a.inreplyto').removeAttr('href').empty().hide();
      }
      tweetpreview.statuslinks = [];
      if (tweetpreview.statustext.match(/^[dD] /)) {
        $('div#tweetpreview').addClass('direct-message');
      } else if ($('div#tweetpreview').hasClass('direct-message')) {
        $('div#tweetpreview').removeClass('direct-message');
      }
      var previewhtml = tweetpreview.statustext.substr(0,160)
                                    .replace(/^[dD] /, '')
                                    .replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&~\+\?\/.=]+(?:#[A-Za-z0-9-_:%&~\+\?\/=]+)?/g, tweetpreview.format_site_links) 
                                    .replace(/<a[^>]+>[^<]+<\/a>/gmi, tweetpreview.format_cut_links)
                                    .replace(/(^|\s|[\?\.,;:\!\-]+)#([^#\?\./,;:`‹›«»~!\[\]\(\)%\+\-\}\{\s]+)/g, tweetpreview.format_hashtag_links)
                                    .replace(/<a[^>]+>[^<]+<\/a>/gmi, tweetpreview.format_cut_links) 
                                    .replace(/(^|[@\?\./,;:~\!\[\]\(\)%\+\-\}\{\s])@([a-zA-Z0-9_]{1,15})/g, '$1@<a href="/$2">$2</a>') 
                                    .replace(/<a>([0-9]+)<\/a>/gmi, tweetpreview.format_paste_links);
      $('span#tweetpreview-message').html(previewhtml);
    } else {
      $('div#tweetpreview').fadeOut('fast');
    }
  },
  
  addstyle: function(styles) { 
    var styleelement = $('style:last');
    styles = styles.join("\n");
    if (!styleelement.length) {
      $('head').append("\n<style type=\"text/css\">\n"+styles+"\n</style>\n");
    } else {
      styleelement.append("\n/*=== TweetPreview ===*/\n"+styles+"\n");
    }
  },

  util: {
    encodehtml: function(str) {
      if (typeof(str) == "string") {
        str = str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#039;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
      }
      return str;
    }
  }
}

tweetpreview.initialize();
unsafeWindow.tweetpreview = tweetpreview;