
    var tweets = new Array();
    var bufTweets = new Array();
    var currentPos;
    var requestTweets = 100;
    var graceNum = 6;
    var scrollOlderClicked = false;
    var scrollNewerClicked = false;

    // Constructor of Tweet
    function Tweet(text, username, id, id_str, created_at)
    {
        this.text = text;
        this.username = username;
        this.id = id;
        this.id_str = id_str;
        this.created_at = created_at;
    }

    // Display 4 tweets starting from idx_start
    function displayTweets(idx_start)
    {
        var oElement = document.getElementById('twitter_update_list');
        if (oElement)
        {
            var statusHTML = [];
            for (var i = idx_start; i < (idx_start + 4); i++)
            {
                var username = tweets[i].username;
                var str;
                var tweetURL = 'http://twitter.com/'+username+'/statuses/'+tweets[i].id_str;
                var status = tweets[i].text;
                status = shorten_tweet(status);
                status = status.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
                                return '<a href="'+url+'" target="_blank">'+url+'</a>';
                            }).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
                                return  reply.charAt(0)+'<a href="http://twitter.com/'+reply.substring(1)+'" target="_blank">'+reply.substring(1)+'</a>';
                            }).replace(/(__more__)/g, function(more) {
                                return '<a href="'+ tweetURL + '" target="_blank">more</a>';
                            });
                    str = '<td style="width:185px;padding:0 5px;"><span style="color:#BEBEBE;">'+status+'</span><br /><a href="' + tweetURL + '" target="_new">'+relative_time(tweets[i].created_at)+'<\/a><\/td>';

                if (navigator.appName == "Microsoft Internet Explorer")
                {
                    if (oElement.cells.length > (i - idx_start))
                    {
                        oElement.deleteCell(i - idx_start);
                    }
                    var oCell = oElement.insertCell(i - idx_start);
                    if (oCell)
                    {
                        oCell.innerHTML = str;
                    }
                }
                else
                {
                    statusHTML.push(str);
                }
            }

            if (navigator.appName != "Microsoft Internet Explorer")
            {
                oElement.innerHTML = statusHTML.join(" ");
            }
        }
    }

    function shorten_tweet(tweetTxt)
    {
        var maxLimit = 110;
        var readMoreReserve = 10;

        if( tweetTxt.length > 110) {
            var words = tweetTxt.split(" ");
            var ret = '';
            for( var i=0; i<words.length && (ret.length + words[i].length + readMoreReserve) < maxLimit ; i++ ) {
                ret += (' ' + words[i]);
            }
            return ret + ' ... __more__';
        } else {
            return tweetTxt;
        }
    }

    function relative_time(time_value)
    {
        var values = time_value.split(" ");
        time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
        var parsed_date = Date.parse(time_value);
        var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
        var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
        delta = delta + (relative_to.getTimezoneOffset() * 60);

        if (delta < 60)
        {
          return 'less than a minute ago';
        } else if(delta < 120)
        {
          return 'about a minute ago';
        } else if(delta < (60*60))
        {
          return (parseInt(delta / 60)).toString() + ' minutes ago';
        } else if(delta < (120*60))
        {
          return 'about an hour ago';
        } else if(delta < (24*60*60))
        {
          return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
        } else if(delta < (48*60*60))
        {
          return '1 day ago';
        } else
        {
          return (parseInt(delta / 86400)).toString() + ' days ago';
        }
    }

    function twitterGet(theUrl)
    {
        var old = document.getElementById('dynamicScript');
        if (old != null)
        {
            old.parentNode.removeChild(old);
            delete old;
        }

        var headID = document.getElementsByTagName("head")[0];
        var newScript = document.createElement('script');
        newScript.type = 'text/javascript';
        newScript.id = 'dynamicScript';
        newScript.src = theUrl;
        headID.appendChild(newScript);
    }

    function clearBufTweets(twitters)
    {
        while (twitters.length > 0)
        {
            twitters.pop();
        }
    }

    function sortTweets(twitters)
    {
        twitters.sort(function (t1, t2){return parseInt(t1.id) < parseInt(t2.id) ? 1:-1});
    }

    function syncAndSaveTweets(twitters)
    {
        if ((!scrollOlderClicked) && (!scrollNewerClicked))
        {
            for (var t = 0; t < twitters.length; t++)
            {
                var tw = new Tweet(twitters[t].text,
                                   twitters[t].username,
                                   twitters[t].id,
                                   twitters[t].id_str,
                                   twitters[t].created_at);
                tweets.push(tw);
            }
    
            currentPos = 0;
            displayTweets(currentPos);
        }
        else if (scrollOlderClicked)
        {
            // Because of twitter user_timeline bug, we request additional(graceNum) tweets.
            // Need to sync before adding to array
            var synced = false;
            scrollOlderClicked = false;
            for (var j = tweets.length - graceNum; (j < tweets.length) && (!synced); j++)
            {
                if (parseInt(twitters[0].id) == parseInt(tweets[j].id))
                {
                    synced = true;
                }
            }
    
            for (var t = tweets.length - j; t < twitters.length; t++)
            {
                var tw = new Tweet(twitters[t].text,
                                   twitters[t].username,
                                   twitters[t].id,
                                   twitters[t].id_str,
                                   twitters[t].created_at);
                tweets.push(tw);
            }
        }
        else if (scrollNewerClicked)
        {
            // if there are newer tweets, insert to array
            var newTweets = 0;
            var newArray = new Array();
    
            scrollNewerClicked = false;
    
            for (var t = 0; t < twitters.length; t++)
            {
                if (parseInt(tweets[0].id) < parseInt(twitters[t].id))
                {
                    newTweets ++;
                    var tw = new Tweet(twitters[t].text,
                                       twitters[t].username,
                                       twitters[t].id,
                                       twitters[t].id_str,
                                       twitters[t].created_at);
                    newArray.push(tw);
                }
            }
    
            if (newTweets > 0)
            {
                tweets = newArray.concat(tweets);
                currentPos += newTweets;
                if (currentPos >= 4)
                {
                    currentPos -= 4;
                }
                else
                {
                    currentPos = 0;
                }
            }
    
            displayTweets(currentPos);
        }
    }
    
    function twitterCallback(twitters)
    {
		if (!twitters)
			return;

		if (twitters[0].user.screen_name == "4INFO")
        {
			// Poll for AdHaven and save 4INFO tweets
			var since_id, max_id;
			since_id = twitters[twitters.length - 1].id;
			max_id = twitters[0].id;
			twitterGet("http://twitter.com/statuses/user_timeline/AdHaven.json?callback=twitterCallback&since_id=" + since_id + "&max_id=" + max_id);
		}

		for (var t = 0; t < twitters.length; t++)
        {
			var tw = new Tweet(twitters[t].text,
							   twitters[t].user.screen_name,
							   twitters[t].id,
							   twitters[t].id_str,
							   twitters[t].created_at);
			bufTweets.push(tw);
		}

		if (twitters[0].user.screen_name != "4INFO")
        {
			sortTweets(bufTweets);
			syncAndSaveTweets(bufTweets);
			clearBufTweets(bufTweets);
		}


    }

$(document).ready(function() {

    // For testing the ability of getting newer tweets after page loaded
    // twitterGet("http://twitter.com/statuses/user_timeline/4INFO.json?callback=twitterCallback&count=" + requestTweets + "&max_id=114010374826770430");

    twitterGet("http://twitter.com/statuses/user_timeline/4INFO.json?callback=twitterCallback&count=" + requestTweets);

    $('#twitter_widget_scroll_older').click(function () {
        scrollOlderClicked = true;
        currentPos += 4;
        displayTweets(currentPos);
        if (currentPos + 10 > tweets.length)
        {
            twitterGet("http://twitter.com/statuses/user_timeline/4INFO.json?callback=twitterCallback&count=" + requestTweets + "&max_id=" + tweets[tweets.length - graceNum].id);
        }
        $('#twitter_widget_scroll_newer').show();
    });

    $('#twitter_widget_scroll_newer').hide().click(function () {
        scrollNewerClicked = true;
        if (currentPos >= 4)
        {
            currentPos -= 4;
            displayTweets(currentPos);
        }
        else
        {
            currentPos = 0;
            // Check for newer tweets
            twitterGet("http://twitter.com/statuses/user_timeline/4INFO.json?callback=twitterCallback&since_id=" + parseInt(tweets[0].id));
        }

        if( currentPos == 0 ) {
            $('#twitter_widget_scroll_newer').hide();
        }
    });
 });


