(function ($) {

    $.fn.tweet = function (o) {
        var s = $.extend({
            username: ["seaofclouds"],                // [string]   required, unless you want to display our tweets. :) it can be an array, just do ["username1","username2","etc"]
            list: null,                               // [string]   optional name of list belonging to username
            favorites: false,                         // [boolean]  display the user's favorites instead of his tweets
            avatar_size: null,                        // [integer]  height and width of avatar if displayed (48px max)
            count: 3,                                 // [integer]  how many tweets to display?
            fetch: null,                              // [integer]  how many tweets to fetch via the API (set this higher than 'count' if using the 'filter' option)
            intro_text: null,                         // [string]   do you want text BEFORE your your tweets?
            outro_text: null,                         // [string]   do you want text AFTER your tweets?
            join_text: null,                         // [string]   optional text in between date and tweet, try setting to "auto"
            auto_join_text_default: "i said,",        // [string]   auto text for non verb: "i said" bullocks
            auto_join_text_ed: "i",                   // [string]   auto text for past tense: "i" surfed
            auto_join_text_ing: "i am",               // [string]   auto tense for present tense: "i was" surfing
            auto_join_text_reply: "i replied to",     // [string]   auto tense for replies: "i replied to" @someone "with"
            auto_join_text_url: "i was looking at",   // [string]   auto tense for urls: "i was looking at" http:...
            loading_text: null,                       // [string]   optional loading text, displayed while tweets load
            query: null,                              // [string]   optional search query
            refresh_interval: null,                  // [integer]  optional number of seconds after which to reload tweets
            twitter_url: "twitter.com",               // [string]   custom twitter url, if any (apigee, etc.)
            twitter_api_url: "api.twitter.com",       // [string]   custom twitter api url, if any (apigee, etc.)
            twitter_search_url: "search.twitter.com", // [string]   custom twitter search url, if any (apigee, etc.)
            template: function (info) {                // [function] template used to construct each tweet <li>
                return info["avatar"] + info["time"] + info["join"] + info["text"];
            },
            comparator: function (tweet1, tweet2) {    // [function] comparator used to sort tweets (see Array.sort)
                return tweet2["tweet_time"] - tweet1["tweet_time"];
            },
            filter: function (tweet) {                 // [function] whether or not to include a particular tweet (be sure to also set 'fetch')
                return true;
            }
        }, o);

        $.fn.extend({
            linkUrl: function () {
                var returning = [];
                // See http://daringfireball.net/2010/07/improved_regex_for_matching_urls
                var regexp = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi;
                this.each(function () {
                    returning.push(this.replace(regexp,
                                      function (match) {
                                          var url = (/^[a-z]+:/i).test(match) ? match : "http://" + match;
                                          return "<a href=\"" + url + "\">" + match + "</a>";
                                      }));
                });
                return $(returning);
            },
            linkUser: function () {
                var returning = [];
                var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
                this.each(function () {
                    returning.push(this.replace(regexp, "<a href=\"http://" + s.twitter_url + "/$1\">@$1</a>"));
                });
                return $(returning);
            },
            linkHash: function () {
                var returning = [];
                var regexp = /(?:^| )[\#]+([A-Za-z0-9-_]+)/gi;
                this.each(function () {
                    returning.push(this.replace(regexp, ' <a href="http://' + s.twitter_search_url + '/search?q=&tag=$1&lang=all&from=' + s.username.join("%2BOR%2B") + '">#$1</a>'));
                });
                return $(returning);
            },
            capAwesome: function () {
                var returning = [];
                this.each(function () {
                    returning.push(this.replace(/\b(awesome)\b/gi, '<span class="awesome">$1</span>'));
                });
                return $(returning);
            },
            capEpic: function () {
                var returning = [];
                this.each(function () {
                    returning.push(this.replace(/\b(epic)\b/gi, '<span class="epic">$1</span>'));
                });
                return $(returning);
            },
            makeHeart: function () {
                var returning = [];
                this.each(function () {
                    returning.push(this.replace(/(&lt;)+[3]/gi, "<tt class='heart'>&#x2665;</tt>"));
                });
                return $(returning);
            }
        });

        function parse_date(date_str) {
            // The non-search twitter APIs return inconsistently-formatted dates, which Date.parse
            // cannot handle in IE. We therefore perform the following transformation:
            // "Wed Apr 29 08:53:31 +0000 2009" => "Wed, Apr 29 2009 08:53:31 +0000"
            return Date.parse(date_str.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3'));
        }

        function relative_time(date) {
            var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
            var delta = parseInt((relative_to.getTime() - date) / 1000, 10);
            var r = '';
            if (delta < 60) {
                r = delta + ' seconds ago';
            } else if (delta < 120) {
                r = 'a minute ago';
            } else if (delta < (45 * 60)) {
                r = (parseInt(delta / 60, 10)).toString() + ' minutes ago';
            } else if (delta < (2 * 60 * 60)) {
                r = 'an hour ago';
            } else if (delta < (24 * 60 * 60)) {
                r = '' + (parseInt(delta / 3600, 10)).toString() + ' hours ago';
            } else if (delta < (48 * 60 * 60)) {
                r = 'a day ago';
            } else {
                r = (parseInt(delta / 86400, 10)).toString() + ' days ago';
            }
            return 'about ' + r;
        }

        function build_url() {
            var proto = ('https:' == document.location.protocol ? 'https:' : 'http:');
            var count = (s.fetch === null) ? s.count : s.fetch;
            if (s.list) {
                return proto + "//" + s.twitter_api_url + "/1/" + s.username[0] + "/lists/" + s.list + "/statuses.json?per_page=" + count + "&callback=?";
            } else if (s.favorites) {
                return proto + "//" + s.twitter_api_url + "/favorites/" + s.username[0] + ".json?count=" + s.count + "&callback=?";
            } else if (s.query === null && s.username.length == 1) {
                return proto + '//' + s.twitter_api_url + '/1/statuses/user_timeline.json?screen_name=' + s.username[0] + '&count=' + count + '&include_rts=1&callback=?';
            } else {
                var query = (s.query || 'from:' + s.username.join(' OR from:'));
                return proto + '//' + s.twitter_search_url + '/search.json?&q=' + encodeURIComponent(query) + '&rpp=' + count + '&callback=?';
            }
        }

        return this.each(function (i, widget) {
            var list = $('<ul class="tweet_list">').appendTo(widget);
            var intro = '<p class="tweet_intro">' + s.intro_text + '</p>';
            var outro = '<p class="tweet_outro">' + s.outro_text + '</p>';
            var loading = $('<p class="loading">' + s.loading_text + '</p>');

            if (typeof (s.username) == "string") {
                s.username = [s.username];
            }

            if (s.loading_text) $(widget).append(loading);
            $(widget).bind("load", function () {
                $.getJSON(build_url(), function (data) {
                    if (s.loading_text) loading.remove();
                    if (s.intro_text) list.before(intro);
                    list.empty();

                    var tweets = $.map(data.results || data, function (item) {
                        var join_text = s.join_text;

                        // auto join text based on verb tense and content
                        if (s.join_text == "auto") {
                            if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) {
                                join_text = s.auto_join_text_reply;
                            } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) {
                                join_text = s.auto_join_text_url;
                            } else if (item.text.match(/^((\w+ed)|just) .*/im)) {
                                join_text = s.auto_join_text_ed;
                            } else if (item.text.match(/^(\w*ing) .*/i)) {
                                join_text = s.auto_join_text_ing;
                            } else {
                                join_text = s.auto_join_text_default;
                            }
                        }

                        // Basic building blocks for constructing tweet <li> using a template
                        var screen_name = item.from_user || item.user.screen_name;
                        var source = item.source;
                        var user_url = "http://" + s.twitter_url + "/" + screen_name;
                        var avatar_size = s.avatar_size;
                        var avatar_url = item.profile_image_url || item.user.profile_image_url;
                        var tweet_url = "http://" + s.twitter_url + "/" + screen_name + "/statuses/" + item.id_str;
                        var tweet_time = parse_date(item.created_at);
                        var tweet_relative_time = relative_time(tweet_time);
                        var tweet_raw_text = item.text;
                        var tweet_text = $([tweet_raw_text]).linkUrl().linkUser().linkHash()[0];

                        // Default spans, and pre-formatted blocks for common layouts
                        var user = '<a class="tweet_user" href="' + user_url + '">' + screen_name + '</a>';
                        var join = ((s.join_text) ? ('<span class="tweet_join"> ' + join_text + ' </span>') : ' ');
                        var avatar = (avatar_size ?
                          ('<a class="tweet_avatar" href="' + user_url + '"><img src="' + avatar_url +
                           '" height="' + avatar_size + '" width="' + avatar_size +
                           '" alt="' + screen_name + '\'s avatar" title="' + screen_name + '\'s avatar" border="0"/></a>') : '');
                        var time = '<span class="tweet_time"><a target="_blank" href="' + tweet_url + '" title="View on Twitter">' + ($.browser.msie ? item.created_at.replace("+0000", "") : dateFormat(item.created_at.replace("+0000", ""), "ddd, mmmm dS, yyyy")) + '</a></span><br />';
                        var text = '<span class="tweet_text">' + $([tweet_text]).makeHeart().capAwesome().capEpic()[0] + '</span>';

                        return { item: item, // For advanced users who want to dig out other info
                            screen_name: screen_name,
                            user_url: user_url,
                            avatar_size: avatar_size,
                            avatar_url: avatar_url,
                            source: source,
                            tweet_url: tweet_url,
                            tweet_time: tweet_time,
                            tweet_relative_time: tweet_relative_time,
                            tweet_raw_text: tweet_raw_text,
                            tweet_text: tweet_text,
                            user: user,
                            join: join,
                            avatar: avatar,
                            time: time,
                            text: text
                        };
                    });

                    tweets = $.grep(tweets, s.filter).slice(0, s.count);
                    list.append($.map(tweets.sort(s.comparator),
                            function (t) { return "<li>" + s.template(t) + "</li>"; }).join('')).
              children('li:first').addClass('tweet_first').end().
              children('li:odd').addClass('tweet_even').end().
              children('li:even').addClass('tweet_odd');

                    if (s.outro_text) list.after(outro);
                    $(widget).trigger("loaded").trigger((tweets.length === 0 ? "empty" : "full"));
                    if (s.refresh_interval) {
                        window.setTimeout(function () { $(widget).trigger("load"); }, 1000 * s.refresh_interval);
                    }
                });
            }).trigger("load");
        });
    };
})(jQuery);

/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
* MIT license
*
* Includes enhancements by Scott Trenda <scott.trenda.net>
* and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/

var dateFormat = function () {
    var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
		    val = String(val);
		    len = len || 2;
		    while (val.length < len) val = "0" + val;
		    return val;
		};

    // Regexes and supporting functions are cached through closure
    return function (date, mask, utc) {
        var dF = dateFormat;

        // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
        if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
            mask = date;
            date = undefined;
        }

        // Passing date through Date applies Date.parse, if necessary
        date = date ? new Date(date) : new Date;
        if (isNaN(date)) throw SyntaxError("invalid date");

        mask = String(dF.masks[mask] || mask || dF.masks["default"]);

        // Allow setting the utc argument via the mask
        if (mask.slice(0, 4) == "UTC:") {
            mask = mask.slice(4);
            utc = true;
        }

        var _ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
			    d: d,
			    dd: pad(d),
			    ddd: dF.i18n.dayNames[D],
			    dddd: dF.i18n.dayNames[D + 7],
			    m: m + 1,
			    mm: pad(m + 1),
			    mmm: dF.i18n.monthNames[m],
			    mmmm: dF.i18n.monthNames[m + 12],
			    yy: String(y).slice(2),
			    yyyy: y,
			    h: H % 12 || 12,
			    hh: pad(H % 12 || 12),
			    H: H,
			    HH: pad(H),
			    M: M,
			    MM: pad(M),
			    s: s,
			    ss: pad(s),
			    l: pad(L, 3),
			    L: pad(L > 99 ? Math.round(L / 10) : L),
			    t: H < 12 ? "a" : "p",
			    tt: H < 12 ? "am" : "pm",
			    T: H < 12 ? "A" : "P",
			    TT: H < 12 ? "AM" : "PM",
			    Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
			    o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
			    S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

        return mask.replace(token, function ($0) {
            return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
        });
    };
} ();

// Some common format strings
dateFormat.masks = {
    "default": "ddd mmm dd yyyy HH:MM:ss",
    shortDate: "m/d/yy",
    mediumDate: "mmm d, yyyy",
    longDate: "mmmm d, yyyy",
    fullDate: "dddd, mmmm d, yyyy",
    shortTime: "h:MM TT",
    mediumTime: "h:MM:ss TT",
    longTime: "h:MM:ss TT Z",
    isoDate: "yyyy-mm-dd",
    isoTime: "HH:MM:ss",
    isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
    isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
    dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
    monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
    return dateFormat(this, mask, utc);
};



