/*
 * jStore - Persistent Client-Side Storage
 *
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 *
 * Dual licensed under:
 * 	MIT: http://www.opensource.org/licenses/mit-license.php
 *	GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
 */
(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=typeof g[d]=="function"&&typeof f[d]=="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}})(d,g[d]):g[d]}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}})();
/*
 * jStore Delegate Framework
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 */
(function(a){this.jStoreDelegate=Class.extend({init:function(b){this.parent=b;this.callbacks={}},bind:function(b,c){if(!a.isFunction(c)){return this}if(!this.callbacks[b]){this.callbacks[b]=[]}this.callbacks[b].push(c);return this},trigger:function(){var d=this.parent,c=[].slice.call(arguments),e=c.shift(),b=this.callbacks[e];if(!b){return false}a.each(b,function(){this.apply(d,c)});return this}})})(jQuery);(function(a){a.jStore={};a.extend(a.jStore,{EngineOrder:[],Availability:{},Engines:{},Instances:{},CurrentEngine:null,defaults:{project:null,engine:null,autoload:true,flash:"jStore.Flash.html"},isReady:false,isFlashReady:false,delegate:new jStoreDelegate(a.jStore).bind("jStore-ready",function(b){a.jStore.isReady=true;if(a.jStore.defaults.autoload){b.connect()}}).bind("flash-ready",function(){a.jStore.isFlashReady=true})});a.jStore.ready=function(b){if(a.jStore.isReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("jStore-ready",b)}};a.jStore.fail=function(b){a.jStore.delegate.bind("jStore-failure",b)};a.jStore.flashReady=function(b){if(a.jStore.isFlashReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("flash-ready",b)}};a.jStore.use=function(d,g,c){g=g||a.jStore.defaults.project||location.hostname.replace(/\./g,"-")||"unknown";var f=a.jStore.Engines[d.toLowerCase()]||null,b=(c?c+".":"")+g+"."+d;if(!f){throw"JSTORE_ENGINE_UNDEFINED"}f=new f(g,b);if(a.jStore.Instances[b]){throw"JSTORE_JRI_CONFLICT"}if(f.isAvailable()){a.jStore.Instances[b]=f;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=f}a.jStore.delegate.trigger("jStore-ready",f)}else{if(!f.autoload){throw"JSTORE_ENGINE_UNAVILABLE"}else{f.included(function(){if(this.isAvailable()){a.jStore.Instances[b]=this;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=this}a.jStore.delegate.trigger("jStore-ready",this)}else{a.jStore.delegate.trigger("jStore-failure",this)}}).include()}}};a.jStore.setCurrentEngine=function(b){if(!a.jStore.Instances.length){return a.jStore.FindEngine()}if(!b&&a.jStore.Instances.length>=1){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[0]);return a.jStore.CurrentEngine=a.jStore.Instances[0]}if(b&&a.jStore.Instances[b]){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[b]);return a.jStore.CurrentEngine=a.jStore.Instances[b]}throw"JSTORE_JRI_NO_MATCH"};a.jStore.FindEngine=function(){a.each(a.jStore.EngineOrder,function(b){if(a.jStore.Availability[this]()){a.jStore.use(this,a.jStore.defaults.project,"default");return false}})};a.store=function(b,c){if(!a.jStore.CurrentEngine){return false}if(!c){return a.jStore.CurrentEngine.get(b)}return a.jStore.CurrentEngine.set(b,c)};a.remove=function(b){if(!a.jStore.CurrentEngine){return false}return a.jStore.CurrentEngine.rem(b)};a.fn.store=function(c,d){if(!a.jStore.CurrentEngine){return this}var b=a.store(c,d);return !d?b:this};a.fn.removeStore=function(b){a.remove(b);return this};a.jStore.load=function(){if(a.jStore.defaults.engine){return a.jStore.use(a.jStore.defaults.engine,a.jStore.defaults.project,"default")}try{a.jStore.FindEngine()}catch(b){}}})(jQuery);(function(a){this.StorageEngine=Class.extend({init:function(c,b){this.project=c;this.jri=b;this.data={};this.limit=-1;this.includes=[];this.delegate=new jStoreDelegate(this).bind("engine-ready",function(){this.isReady=true}).bind("engine-included",function(){this.hasIncluded=true});this.autoload=false;this.isReady=false;this.hasIncluded=false},include:function(){var b=this,d=this.includes.length,c=0;a.each(this.includes,function(){a.ajax({type:"get",url:this,dataType:"script",cache:true,success:function(){c++;if(c==d){b.delegate.trigger("engine-included")}}})})},isAvailable:function(){return false},ready:function(b){if(this.isReady){b.apply(this)}else{this.delegate.bind("engine-ready",b)}return this},included:function(b){if(this.hasIncluded){b.apply(this)}else{this.delegate.bind("engine-included",b)}return this},get:function(b){return this.data[b]||null},set:function(b,c){this.data[b]=c;return c},rem:function(b){var c=this.data[b];this.data[b]=null;return c}})})(jQuery);
/*
 * jStore DOM Storage Engine
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 */
(function(c){var b=c.jStore.Availability.session=function(){return !!window.sessionStorage},a=c.jStore.Availability.local=function(){return !!(window.localStorage||window.globalStorage)};this.jStoreDom=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="DOM";this.limit=5*1024*1024},connect:function(){this.delegate.trigger("engine-ready")},get:function(e){var d=this.db.getItem(e);return d&&d.value?d.value:d},set:function(d,e){this.db.setItem(d,e);return e},rem:function(e){var d=this.get(e);this.db.removeItem(e);return d}});this.jStoreLocal=jStoreDom.extend({connect:function(){this.db=!window.globalStorage?window.localStorage:window.globalStorage[location.hostname];this._super()},isAvailable:a});this.jStoreSession=jStoreDom.extend({connect:function(){this.db=sessionStorage;this._super()},isAvailable:b});c.jStore.Engines.local=jStoreLocal;c.jStore.Engines.session=jStoreSession;c.jStore.EngineOrder[1]="local"})(jQuery);
/*
 * jStore Google Gears Storage Engine
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 */
(function(b){var a=b.jStore.Availability.gears=function(){return !!(window.google&&window.google.gears)};this.jStoreGears=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="Google Gears";this.includes.push("http://code.google.com/apis/gears/gears_init.js");this.autoload=true},connect:function(){var c=this.db=google.gears.factory.create("beta.database");c.open("jstore-"+this.project);c.execute("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)");this.updateCache()},updateCache:function(){var c=this.db.execute("SELECT k,v FROM jstore");while(c.isValidRow()){this.data[c.field(0)]=c.field(1);c.next()}c.close();this.delegate.trigger("engine-ready")},isAvailable:a,set:function(d,e){var c=this.db;c.execute("BEGIN");c.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[d,e]);c.execute("COMMIT");return this._super(d,e)},rem:function(d){var c=this.db;c.execute("BEGIN");c.execute("DELETE FROM jstore WHERE k = ?",[d]);c.execute("COMMIT");return this._super(d)}});b.jStore.Engines.gears=jStoreGears;b.jStore.EngineOrder[3]="gears"})(jQuery);
/*
 * jStore HTML5 Specification Storage Engine
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 */
(function(b){var a=b.jStore.Availability.html5=function(){return !!window.openDatabase};this.jStoreHtml5=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="HTML5";this.limit=1024*200},connect:function(){var c=this.db=openDatabase("jstore-"+this.project,"1.0",this.project,this.limit);if(!c){throw"JSTORE_ENGINE_HTML5_NODB"}c.transaction(function(d){d.executeSql("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this.updateCache()},updateCache:function(){var c=this;this.db.transaction(function(d){d.executeSql("SELECT k,v FROM jstore",[],function(f,e){var h=e.rows,g=0,j;for(;g<h.length;++g){j=h.item(g);c.data[j.k]=j.v}c.delegate.trigger("engine-ready")})})},isAvailable:a,set:function(c,d){this.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[c,d])});return this._super(c,d)},rem:function(c){this.db.transaction(function(d){d.executeSql("DELETE FROM jstore WHERE k = ?",[c])});return this._super(c)}});b.jStore.Engines.html5=jStoreHtml5;b.jStore.EngineOrder[0]="html5"})(jQuery);
/**
 * jStore IE Storage Engine
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 */
(function(b){var a=b.jStore.Availability.ie=function(){return !!window.ActiveXObject};this.jStoreIE=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="IE";this.limit=64*1024},connect:function(){this.db=b('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+this.project+'"></div>').appendTo(document.body).get(0);this.delegate.trigger("engine-ready")},isAvailable:a,get:function(c){this.db.load(this.project);return this.db.getAttribute(c)},set:function(c,d){this.db.setAttribute(c,d);this.db.save(this.project);return d},rem:function(c){var d=this.get(c);this.db.removeAttribute(c);this.db.save(this.project);return d}});b.jStore.Engines.ie=jStoreIE;b.jStore.EngineOrder[4]="ie"})(jQuery);


(function ($) {
    var m = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    },
        s = {
            'array': function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            'number': function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            'object': function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            'string': function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                        Math.floor(c / 16).toString(16) +
                        (c % 16).toString(16);
                    });
        }
        return '"' + x + '"';
}
 };

$.toJSON = function(v) {
    var f = isNaN(v) ? s[typeof v] : s['number'];
    if (f) return f(v);
};

$.parseJSON = function(v, safe) {
    if (safe === undefined) safe = $.parseJSON.safe;
    if (safe && !/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(v))
return undefined;
return eval('('+v+')');
};

$.parseJSON.safe = false;

})(jQuery);

var parseJson = function(str) {
    return $.parseJSON(str);
}

var stringifyJson = function(obj) {
    return $.toJSON(obj);
}

if (window.JSON) {
    var parseJson = function(str) {
        return JSON.parse(str);
    }

    var stringifyJson = function(obj) {
        return JSON.stringify(obj);
    }
}

/*
 * timeago: a jQuery plugin, version: 0.7.1 (2009-02-18)
 * @requires jQuery v1.2 or later
 *
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2008-2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
 */
(function($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) return inWords(timestamp);
    else if (typeof timestamp == "string") return inWords($.timeago.parse(timestamp));
    else return inWords($.timeago.parse($(timestamp).attr("title")));
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowFuture: false,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        ago: null, // DEPRECATED, use suffixAgo
        fromNow: null, // DEPRECATED, use suffixFromNow
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years"
      }
    },
    inWords: function(distanceMillis) {
      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo || $l.ago;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow || $l.fromNow;
        }
        distanceMillis = Math.abs(distanceMillis);
      }

      var seconds = distanceMillis / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 48 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.floor(days)) ||
        days < 60 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
        years < 2 && substitute($l.year, 1) ||
        substitute($l.years, Math.floor(years));

      return $.trim([prefix, words, suffix].join(" "));
    },
    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      return new Date(s);
    }
  });

  $.fn.timeago = function() {
    var self = this;
    self.each(refresh);

    var $s = $t.settings;
    if ($s.refreshMillis > 0) {
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
    }
    return self;
  };

  function refresh() {
    var date = $t.parse(this.title);
    if (!isNaN(date)) {
      $(this).text(inWords(date));
    }
    return this;
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  function substitute(stringOrFunction, value) {
    var string = $.isFunction(stringOrFunction) ? stringOrFunction(value) : stringOrFunction;
    return string.replace(/%d/i, value);
  }

  // fix for IE6 suckage
  if ($.browser.msie && $.browser.version < 7.0) {
    document.createElement('abbr');
  }
})(jQuery);


var queueData = false;

function tweetList(domain, engine) {

    var key = username,
        maxkey = username + '_max';

    return {
        setMax: function(id) {
            engine.set(maxkey, id);
        },
        getMax: function() {
            return engine.get(maxkey) || 0;
        },
        push: function(obj) {
            var data, json, result;

            result = engine.get(key);

            if (!result) {
                data = [];
            } else {
                data = parseJson(result);
            }

            data.push(obj);

            json = stringifyJson(data);

            engine.set(key, json);

        },
        unshift: function(obj, queue) {
            var data, json, result;

            if (queue) {

                if (!queueData) {

                    result = engine.get(key);

                    if (!result) {
                        queueData = [];
                    } else {
                        queueData = parseJson(result);
                    }
                }

                queueData.unshift(obj);

            } else {

                result = engine.get(key);

                if (!result) {
                    data = [];
                } else {
                    data = parseJson(result);
                }

                data.unshift(obj);

                json = stringifyJson(data);
                engine.set(key, json);
            }

        },
        fetchAll: function() {

            var result = engine.get(key);

            if (result && result != '') {
                return parseJson(result);
            }

            return [];
        },
        save: function() {
            if (queueData) {
                queueData = queueData.slice(0, 200);
                json = stringifyJson(queueData);
                engine.set(key, json);
            }
        },
        reset: function() {
            engine.set(key, '');
            engine.set(maxkey, '');
        }

    };

}



var g_domain = document.location.hostname,
    list,
    unread = 0,
    htmlTitle = 'twss',
    in_reply_to_status_id = null,
    jstoreGood = false,
    g_sg_ajax_open = 0;

Date.prototype.toISO8601String = function (format, offset) {
    /* accepted values for the format [1-6]:
     1 Year:
       YYYY (eg 1997)
     2 Year and month:
       YYYY-MM (eg 1997-07)
     3 Complete date:
       YYYY-MM-DD (eg 1997-07-16)
     4 Complete date plus hours and minutes:
       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
     5 Complete date plus hours, minutes and seconds:
       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     6 Complete date plus hours, minutes, seconds and a decimal
       fraction of a second
       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
    */
    if (!format) { var format = 6; }
    if (!offset) {
        var offset = 'Z';
        var date = this;
    } else {
        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
        offsetnum *= ((d[1] == '-') ? -1 : 1);
        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
    }

    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }

    var str = "";
    str += date.getUTCFullYear();
    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
    if (format > 3) {
        str += "T" + zeropad(date.getUTCHours()) +
               ":" + zeropad(date.getUTCMinutes());
    }
    if (format > 5) {
        var secs = Number(date.getUTCSeconds() + "." +
                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
                   zeropad(date.getUTCMilliseconds()));
        str += ":" + zeropad(secs);
    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }

    if (format > 3) { str += offset; }
    return str;
}

function linkify(str) {

    var hosts = [
        'tinyurl.com',
        'is.gd',
        'ur1.ca',
        'ping.fm',
        'snipurl.com',
        'snurl.com',
        'snipr.com',
        'sn.im',
        'bit.ly',
        'tr.im',
        'xrl.us',
        'twurl.nl',
        'x.se',
        'poprl.com',
        'url.ie',
        '6url.com',
        'yep.it',
        'ln-s.net',
        'piurl.com',
        'yatuc.com',
        'g8l.us',
        'icanhaz.com',
        'urlkiss.com',
        'minilien.com',
        'tinylink.com',
        'urlcut.com',
        'doiop.com',
        'smurl.com',
        'tighturl.com',
        '2tu.us',
        'myurl.in',
        'memurl.com',
        'redirx.com',
        'easyurl.net',
        'qurlyq.com',
        'dwarfurl.com',
        'shrinkurl.us',
        'starturl.com',
        'urlhawk.com',
        'canurl.com',
        'surl.co.uk',
        'lnkurl.com',
        'urlbrief.com',
        'urlborg.com',
        'ub0.cc',
        'urlvi.be',
        'sn.vc',
        'lurl.no',
        'hurl.ws',
        'twiturl.de',
        'spedr.com',
        'parv.us',
        'decenturl.com',
        'shorterlink.com',
        'shortlinks.co.uk',
        'budurl.com',
        'shw.me',
        'buk.me',
        'zz.gd',
        '0rz.tw',
        'lin.cr',
        'shink.de',
        's3nt.com',
        'fff.to',
        'wipi.es',
        'ri.ms',
        'b23.ru',
        'zi.ma',
        'srs.li',
        'liip.to',
        'post.ly',
        'tcrn.ch',
        'cli.gs',
        'ptiturl.com',
        'snadr.it',
        'shrinkify.com',
        's7y.us',
        'tiny.cc',
        'idek.net',
        'ff.im',
        'onsaas.info',
        'tubeurl.com',
        'tgr.me',
        'ow.ly',
        'a2n.eu',
        'azqq.com',
        'liltext.com',
        'b65.com',
        'cuturls.com',
        'goshrink.com',
        'atu.ca',
        'redirects.ca',
        'fhurl.com',
        'flingk.com',
        'fly2.ws',
        'nutshellurl.com',
        'notlong.com',
        'nanoref.com',
        'moourl.com',
        'shurl.net',
        'shrinkr.com',
        'shortenurl.com',
        'rubyurl.com',
        'url.co.uk',
        'xil.in',
        'wapurl.co.uk',
        'u76.org',
        'urlcutter.com',
        'fwdurl.net',
        'shrtnd.com',
        'urlpire.com',
        'smallr.com',
        'shredurl.com',
        'linkgap.com',
        'plumurl.com',
        'offur.com',
        'liurl.cn',
        'adjix.com',
        'ad.vu',
        'togoto.us',
        'a.gg',
        'a.nf',
        'go.9nl.com',
        'lru.jp',
        'kl.am',
        'nn.nf',
        'hex.io',
        'to.ly',
        'digg.com',
        'cliccami.info',
        '➡.ws',
        '➨.ws',
        '➯.ws',
        '➔.ws',
        '➞.ws',
        '➽.ws',
        '➹.ws',
        '✩.ws',
        '✿.ws',
        '❥.ws',
        '›.ws',
        'ta.gd',
        'cort.as',
        'u.nu',
        'r.im',
        'alturl.com',
        'chilp.it',
        'ewerl.com',
        'flq.us',
        'hugeurl.com',
        'jijr.com',
        'kissa.be',
        'l9k.net',
        'o-x.fr',
        'omf.gd',
        'plurl.me',
        'rb6.me',
        'rickroll.it',
        'shoturl.us',
        'srnk.net',
        'tiny.pl',
        'u.mavrev.com',
        'updating.me',
        'xrl.in',
        'xurl.jp',
        //'yfrog.com',
        '307.to',
        'bacn.me',
        'bloat.me',
        'clipurl.us',
        'href.in',
        'korta.nu',
        'merky.de',
        'nanourl.se',
        'peaurl.com',
        'pnt.me',
        'rde.me',
        'reallytinyurl.com',
        'redir.ec',
        'short.ie',
        'short.to',
        'tra.kz',
        'twurl.cc',
        'url.az',
        'urlx.ie',
        'zurl.ws',
        'qlnk.net',
        'twitclicks.com',
        'htxt.it',
        'fwd4.me',
        'clop.in',
        'ln-s.ru',
        'sp2.ro',
        'eepurl.com',
        'twitterurl.net',
        'clck.ru',
        'netnet.me',
        'adf.ly',
        'zzang.kr',
        'ni.to',
        'lu.to',
        'virl.com',
        'gl.am',
        'klck.me',
        'trunc.it',
        'migre.me',
        'xzb.cc',
        'ulu.lu'
    ],
    i = 0,
    host = '',
    re;

    if (!str) {
	return '';
    }

    for (i = 0; i < hosts.length; ++i) {
	host = hosts[i];

	re = new RegExp('(http://' + host.replace('.', '\\' + '.') + '/([^ \\.]+))', 'ig');
	str = str.replace(re, '$1 <a href="#" class="url_expand" rel="' + host + '" title="$1">+</a>');

    }

    // twitpic
    str = str.replace(/(http:\/\/(www\.)?twitpic\.com\/([^ \.]+))/ig, '<a target="_blank" href="$1"><img src="http://twitpic.com/show/mini/$3" /></a>');

    // yfrog
    str = str.replace(/(http:\/\/(www\.)?yfrog\.com\/([^ \.]+))/ig, '<a target="_blank" href="$1"><img src="http://yfrog.com/$3.th.jpg" /></a>');

    // hash tags
    str = str.replace(/ #([^ ]+)/ig, ' <a target="_blank" href="http://search.twitter.com/search?q=%23$1">#$1</a>');
    str = str.replace(/^#([^ ]+)/ig, '<a target="_blank" href="http://search.twitter.com/search?q=%23$1">#$1</a>');

    // start
    str = str.replace(/(^|\s+)(https*\:\/\/\S+[^\.\s+])/ig, '$1<a target="_blank" href="$2">$2</a>');
    str = str.replace(/ (([a-z0-9\-]+\.)+[a-z]+\/*([^ ]+[^\. ]+)?)/ig, ' <a target="_blank" href="http://$1">$1</a>');
    //str = str.replace(/(^|\s+)(S+[^\.\s+])($|\s+|\.)/ig, '$1<a target="_blank" href="http://$2">$2</a>$3');

    // twitter usernames
    str = str.replace(/(@([a-z0-9_]+))/ig, '<a target="_blank" href="http://twitter.com/$2">$1</a>');

    return str;
}

$(function() {

    function setTitle(title) {
	document.getElementsByTagName('title')[0].text = title;
    }

    function pullTweets() {

	var data = {},
	    since_id,
	    options;

	since_id = parseInt(list.getMax());
	if (since_id && since_id > 0) {
	    data.since_id = since_id;
	}

	options = {
	    type: 'GET',
	    url: 'ajax.php',
	    dataType: 'json',
	    success: results,
	    data: data
	};

	ajax_start();
	$.ajax(options);

	return false;
    }

    function results(data) {

	var content = '',
	    first = true,
	    row;

	ajax_end();

	if (data.error) {
	    return;
	}

	unread = unread + data.length;

	if (unread > 0) {
	    setTitle(htmlTitle + ' (' + unread + ')');
	} else {
	    setTitle(htmlTitle);
	}

	$.each(data, function() {

	    if (first) {
		list.setMax(this.id);
	    }

	    content += renderTweet(this, true);
	    first = false;

	});

	$('#list').prepend(content);
	$('#list .post').slice(200).remove();
	$('.timeago').timeago().removeClass('timeago');

	data.reverse();
	$.each(data, function() {

	    row = {};
	    row.id = this.id;
	    row.text = this.text;
	    row.created_at = this.created_at;
	    row.user = {};
	    row.user.profile_image_url = this.user.profile_image_url;
	    row.user.screen_name = this.user.screen_name;
	    row.source = this.source;

	    list.unshift(row, true);
	});

	list.save();

    }

    function renderTweet(item, newclass) {

	var content = '',
	    classname = '',
	    img = null,
	    text;

	if (!item.user) {
	    return '';
	}

	if (newclass) {
	    classname = ' new';
	}

	text = linkify(item.text);

	if (item.user && item.user.profile_image_url) {
	    img = item.user.profile_image_url;
	}

	if (img === 'http://static.twitter.com/images/default_profile_normal.png') {
	    img = 'images/summers_eve.jpg';
	}

	content += '<div class="post' + classname + '">';
	if (item.id) {
	    content += '<img src="images/reply.png" rel="' + item.id + '" author="' + item.user.screen_name + '" class="right reply pointer" />';
	}
	content += '<div class="img"><a target="_blank" href="http://twitter.com/' + item.user.screen_name + '"><img src="' + img + '" width="48" height="48" /></a></div>';
	content += '<div class="status"><p><a target="_blank" href="http://twitter.com/' + item.user.screen_name + '">' + item.user.screen_name + '</a>: ';
	content += text + '</p>';

	if (item.source) {
	    content += '<p class="source">Posted from <i>' + item.source + '</i>';
	    content += ' <span class="timeago" title="' + new Date(item.created_at).toISO8601String() + '">' + item.created_at + '</span>';
	    content += '</p>';
	}

	content += '</div>';
	content += '</div>';

	return content;

    }

    function displayExisting() {

	var content = '',
	    data = list.fetchAll();

	$.each(data, function() {
	    content += renderTweet(this);
	});

	$('#list').html(content);
	$('.timeago').timeago().removeClass('timeago');

    }

    $('.pull').click(pullTweets);

    $('.write').click(function() {
        $('#authoring').slideDown();
	$('#post_src').val('').focus();
	$('#char_count').text('140');
	in_reply_to_status_id = null;
        return false;
    });

    $('#close_post').click(function() {
	$('#authoring').slideUp();
	in_reply_to_status_id = null;
	return false;
    });

    $('#send_post').click(function() {

	var data = {},
	    options;

	data.status = $('#post_src').val();

	if (in_reply_to_status_id) {
	    data.in_reply_to_status_id = in_reply_to_status_id;
	}

	options = {
	    type: 'POST',
	    url: 'post.php',
	    dataType: 'json',
	    success: function() {
		$('#authoring').slideUp();
		ajax_end();
		if (!ajax_pending()) {
		    setTimeout(pullTweets, 1000);
		}
	    },
	    data: data
	};

	ajax_start();
	$.ajax(options);

	return false;
    });

    $('#post_src').keyup(function() {
	$('#char_count').text(140 - parseInt($(this).val().length));
    });

    $('.reply').live('click', function() {
	var text = '@' + $(this).attr('author') + ' ';
	in_reply_to_status_id = $(this).attr('rel');
	$('#authoring').slideDown();
	$('#post_src').val(text).focus();
	$('#char_count').text(140 - text.length);
	return false;
    });

    jQuery.jStore.ready(function(engine){
    if (good) {

	list = tweetList(g_domain, engine);
	setTimeout(displayExisting, 100);

	jstoreGood = true;

	setTimeout(pullTweets, 1000);
	setInterval(pullTweets, 200000);
    }
    });

    jQuery.jStore.load();

    function checkPlainMode() {
	if (!jstoreGood) {
	    initPlainMode();
	}
    }

    setTimeout(checkPlainMode, 1000);

    function initPlainMode() {

	var engine = plainModeEngine();
	list = tweetList(g_domain, engine);

	setTimeout(pullTweets, 1000);
	setInterval(pullTweets, 200000);

    }

    var long_url_expand = function (url, $el) {
	var options = {
	    type: 'GET',
	    url: 'http://api.longurl.org/v1/expand',
	    dataType: 'jsonp',
	    data: {
		format: 'json',
		url: url
	    },
	    success: function(data) {
		ajax_end();
		if (data && data.long_url) {
		    $el.prev().text(data.long_url);
		    $el.prev().attr('title', data.title);
		    $el.hide();
		}
	    }
	};

	ajax_start();
	$.ajax(options);
    };

    $('.url_expand').live('click', function() {

	var $el = $(this),
	    url = $el.attr('title');

	long_url_expand(url, $el);

	return false;
    });

    $(document).mousedown(function() {
	unread = 0;
	setTitle(htmlTitle);
	$('.new').removeClass('new');
    });

});

var plainModeEngine = function () {

    var data = {};

    return {
	set: function(key, string) {
	    data[key] = string;
	},
	get: function(key) {
	    return data[key] || null;
	}

    };
};

function ajax_pending() {
    return (g_sg_ajax_open > 0);
}

function ajax_start() {
    ++g_sg_ajax_open;
    $('.spinner').show();
}

function ajax_end() {
    --g_sg_ajax_open;
    if (g_sg_ajax_open === 0) {
        $('.spinner').hide();
    }
}


