var c = window.console;
/**
 * @author pete
 * Code for the widgets/forms that send tweets to twitter.  Takes the
 * id of the particular widget, the auth state of the user, the default
 * text of the tweet, and the url on tweetswirl that prcoesses
 * the request.  Most of the functions are private, or static if necessary
 * to reduce weight and issues with other stuff.  The goal is to be
 * very self contained so it can work or fail "atomically".
 */
var TweetWidget = new Class({
    
    setMessageValue: function(val) {
        this.textArea.value = val + " ";
        this.textArea.focus();  
    },
    
    retweet: function(id) {
        if (!window.TS_auth) {
            //create the buttons
            var buttons = '<div class="buttons">';
            buttons += '<a href="javascript:;" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');">Cancel</a> | ';
            buttons += '<input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');Tweetswirl.openOauthWindow();">';
            buttons += '</div>';
            //append them to the message
            var content = {
                'title':'Login to Your twitter Account',
                'content': this.messages['requirelogin'] + buttons
            };
            //create the modal
            TweetSwirl.updateModal(this.modal_id, content);
            this.textArea.blur();
        } else {
            var request = new Request({
               url: this.retweet_url+id,
               data: '',
               method: 'get',
               onSuccess: this.processSuccess.bind(this),
               onError: this.processError.bind(this)
            });
            request.send();
        }
        return false;
    },
    
    attachEvents: function(){
        //if the character count element is actually displayed
        if(this.characterCount) {
            //add a key down event handler to update the count accordingly
            this.textArea.addEvents({
                'keypress': this.imposeMaxLength.bindWithEvent(this),
                'keyup': this.resetTweetCount.bind(this),
                'blur': this.resetTweetCount.bind(this),
                'click': (function(event) {
                    if(window.TS_auth && this.initialInteraction) {
                        this.initialInteraction = false;
                        this.textArea.value = '';
                        this.textArea.removeClass("initial");
                    }
                    this.resetTweetCount();
                }).bindWithEvent(this),
                'focus': (function(event) {
                    if (!window.TS_auth) {
                        //create the buttons
                        var buttons = '<div class="buttons">';
                        buttons += '<a href="javascript:;" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');">Cancel</a> | ';
                        buttons += '<input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');TweetSwirl.openOauthWindow()">';
                        buttons += '</div>';
                        //append them to the message
                        var content = {
                            'title':'Login to Your twitter Account',
                            'content': this.messages['requirelogin'] + buttons
                        };
                        //create the modal
                        TweetSwirl.updateModal(this.modal_id, content);
                        this.textArea.blur();
                    }
                }).bind(this)
            });
        }
        
        this.sendButton.addEvents({
            'click': (function(event){
                event.stop();
                //button click --> login
                if (!window.TS_auth) {
                    //create the buttons
                    var buttons = '<div class="buttons">';
                    buttons += '<a href="javascript:;" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');">Cancel</a> | ';
                    buttons += '<input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');TweetSwirl.openOauthWindow();">';
                    buttons += '</div>';
                    //append them to the message
                    var content = {
                        'title':'Login to Your twitter Account',
                        'content': this.messages['requirelogin'] + buttons
                    };
                    //create the modal
                    TweetSwirl.updateModal(this.modal_id, content);
                    this.textArea.blur();
                } else {
                    //button click --> validate\post
                    var msg = this.textArea.value;
                    if (msg != '' && msg.match('([0-9a-zA-Z])+') && this.validateTweet(msg)) {
                        this.sendTweet(encodeURIComponent(msg));
                    }
                }
            }).bind(this)
        });
    },
    
    //function taken from http://blog.listcentral.me/2009/10/05/enforcing-max-length-on-a-textarea/
    imposeMaxLength: function (event) {
        var whitelist = ['backspace', 'up', 'down', 'left', 'right', 'delete'];
        if (whitelist.contains(event.key)) {
            return true;
        } else {
            return (this.textArea.value.length < this.maxLength);
        }
    },

    
    /**
     * Send a tweet message by calling the server and using the Twitter Api.
     * @param {String} msg - the actual tweet message to send
     */
    sendTweet: function(msg){
        var request = new Request({
           url: this.url,
           data: 'msg='+msg,
           method: 'get',
           onSuccess: this.processSuccess.bind(this),
           onError: this.processError.bind(this)
        });
        request.send();
        
    },    
    processSuccess: function(responseText) {
        //update the modal
        var button = '<div class="buttons"><input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');"></div>';
        
        var content = {
            'title':'',
            'content':responseText + button
        };
        TweetSwirl.updateModal(this.modal_id, content);
        this.resetTweetForm();
    },
    
    processError: function(responseText) {
        //update the modal
        var button = '<div class="buttons"><input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');"></div>';
        var content = {
            'title':'',
            'content':responseText + button
        };
        TweetSwirl.updateModal(this.modal_id, content);
        this.resetTweetForm();
    },
    
    resetTweetForm: function() {
        //reset the message and character count
        this.textArea.value = this.default_message;
        this.resetTweetCount();
    },
    
    resetTweetCount: function() {
        this.characterCount.innerHTML = (140 - this.textArea.value.length) + ' ' + this.countMessage;
    },
    
    /**
     * Validate the tweet message on the client side, make sure it can be
     * encoded and that the encoded version is less than 140 characters.
     * @param {String} msg
     */
    validateTweet: function(msg){
        //check that the msg can be encoded.
        var orig_msg = msg;
        var valid = true;
        var modal_content = '';
        var button = '<div class="buttons"><input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');"></div>';
        try {
            msg = encodeURIComponent(orig_msg);
        } catch(e) {
            //show the modal
            modal_content = this.messages['tweetproblem'];
            modal_content += button;
            valid = false;    
        }
        //check if the msg is <= 140 characters encoded
        if(orig_msg.length > this.maxLength) {
            modal_content = this.messages['tweettoolong'];
            modal_content += button;
            valid = false;
        }
        
        //show a modal is something went wrong
        if (modal_content != '') {
            var content = {
                'title': '',
                'content': modal_content
            };
            TweetSwirl.updateModal(this.modal_id, content);
        }
        return valid;
    
    },
        
    initialize: function(conf) {
        if ($(conf['id'])) {            
            var cont = this.cont = $(conf['id']);
            
            this.maxLength = 140;
            this.countMessage = conf['countMessage'] || '';
            this.url = conf['url'];
            this.retweet_url = conf['retweet_url'];
            this.initialInteraction = true;
            this.initialMessage = conf['initial_message'];
            this.default_message = conf['default_message'];
            
            this.textArea = cont.getElement('.tweetMessage');
            this.characterCount = cont.getElement('.character_count');
            this.sendButton = cont.getElement('button');
            
            if(this.initialMessage) {
                this.textArea.value = this.initialMessage;
                this.textArea.addClass("initial");
            } else {
                //make it as if we've already interacted so it dosen't get reset
                //when there is no initialMessage
                this.initialInteraction = false;
            }
            
            
            //get messages
            this.messages = conf['messages'];
            // Create modal
            this.modal_id = TweetSwirl.createModal('','modal_hide');
            this.attachEvents();
            this.resetTweetCount();
        } else {
            return null;
        }
    }
});


var UserRequest_TweetWidget = new Class({
    Extends: TweetWidget,
    
    /**
     * Search for the given phrase in the given element
     * and highlight it, then give the element focus
     */
    highlightPhrase: function(el, phrase) {
        if(this.textArea.value.contains(this.phrase)) {
            //get the starting point
            var start = this.textArea.value.indexOf(this.phrase);
            this.textArea.selectRange(start, start+this.phrase.length);
        }
    },
    
    /**
     * Override the add events method to check for the @TwitterUser and
     * highlight it if its present.  Then call the parent method to add
     * all the regular methods.
     */
    attachEvents: function() {
        this.textArea.addEvent(
            'click', this.highlightPhrase.bind(this, [this.textArea, this.phrase])
        );
        this.parent();
    },
    
    /**
     * Overide the validateTweet function to look for @TwitterUser.  If 
     * it is present in the message it needs to be highlighted before sending.
     * If no we can use the parent's validateTweet method since the requirements
     * are the same.
     */
    validateTweet: function(msg) {
        if(this.textArea.value.contains(this.phrase)) {
            this.highlightPhrase(this.textArea, this.phrase);
            return false;
        }
        return this.parent(msg);  
    },
    initialize: function(conf) {
        this.parent(conf);
        this.phrase = conf['phrase'];
    }
});


/**
 * @author pete
 * Code for the top requested widget.  This is very similar to the
 * tweet widget, but for now its just copy and paste.  This does the same
 * sort of checking for logged in, but otherwise it just sends a hardcoded
 * tweet.
 */
var TopRequestedWidget = new Class({

    attachEvents: function(thumblink){
        thumblink.addEvents({
            'click': (function(event){
                event.stop();
                //button click --> login
                if (!window.TS_auth) {
                    //create the buttons
                    var buttons = '<div class="buttons">';
                    buttons += '<a href="javascript:;" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');">Cancel</a> | ';
                    buttons += '<input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');TweetSwirl.openOauthWindow();">';
                    buttons += '</div>';
                    //append them to the message
                    var content = {
                        'title': 'Login to your Twitter Account',
                        'content': this.messages['requirelogin'] + buttons
                    };
                    //create the modal
                    TweetSwirl.updateModal(this.modal_id, content);
                } else {
                    //button click --> validate\post
                    var href = thumblink.href;
                    var msg = href.substring(href.indexOf('@tweetswirl'), href.indexOf('&after'));
                    var content = {
                        'title': 'Login to your Twitter Account',
                        'content': this.messages['tweetsending']
                    };
                    TweetSwirl.updateModal(this.modal_id, content);
                    this.sendTweet(msg);
                }
            }).bind(this)
        });
    },
    
    /**
     * Send a tweet message by calling the server and using the Twitter Api.
     * @param {String} msg - the actual tweet message to send
     */
    sendTweet: function(msg){
        var request = new Request({
           url: this.url,
           data: 'msg='+msg,
           method: 'get',
           onSuccess: this.processSuccess.bind(this),
           onError: this.processError.bind(this)
        });
        request.send();
    },
    
    processSuccess: function(responseText) {
        //update the modal
        var button = '<div class="buttons"><input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');"></div>';
        var content = {
            'title': 'Success',
            'content': responseText + button
        };
        TweetSwirl.updateModal(this.modal_id, content);
    },
    
    processError: function(responseText) {
        //update the modal
        var button = '<div class="buttons"><input type="image" src="../images/common/btnOk.gif" onclick="TweetSwirl.hideModal(\''+this.modal_id+'\');"></div>';
        var content = {
            'title': 'Error',
            'content': responseText + button
        };
    },
    
    initialize: function(conf) {
        if ($(conf['id'])) {
            
            var cont = this.cont = $(conf['id']);
            this.url = conf['url'];
            this.default_message = conf['default_message'];
            
            //get messages
            this.messages = conf['messages'];
            // Create modal
            this.modal_id = TweetSwirl.createModal('','modal_hide');
            
            //get the links and attach the events
            var thumbs = cont.getElements('.showbutton');
            thumbs.each(this.attachEvents, this);
        } else {
            return null;
        }
    }
});

/**
 * @author roel
 * Code for widget that will filter/search for feeds
 */
var FilterWidget = new Class({
    
    /**
     * Setup widget by creating class variables and adding events to DOM elements
     */
    setupWidget: function() {
        this.scrollLengthFull = 0;
        this.scrollPartial = 0;
        this.scrollMaxPosition = 0;
        
        this.selectedImageCurrent = null;
        this.selectedImageNext = null;
        this.selectedImageOffset = 4;
        
        this.AJAXRefreshInterval = null;
        this.FilmstripAutoplayInterval = null;
        
        this.setupElements();
        this.checkURLFilter();
        this.filmstripCalculations();
        this.attachEvents();
        this.updateArrows();  
    },
    
    /**
     * Retreive elements in widghet
     */
    setupElements: function() { 
        this.feeds = this.conf['id'];
        this.url = this.conf['url'];
        this.participant_name = this.conf['participant_name'];
        this.page_name = this.conf['page_name'];
        
        this.element = {
            'hub_left': $('hubLeft'),
            'search_filters': $$('#feeds .header .search_filters')[0],
            'filmstrip': $$('#feeds .participant_filters .filmstrip')[0],
            'filmstrip_viewport': $$('#feeds .participant_filters .filmstrip .viewport')[0],
            'filmstrip_gallery': $$('#feeds .participant_filters .filmstrip .viewport .gallery')[0],
            'filmstrip_thumbnails': $$('#feeds .participant_filters .filmstrip .viewport .gallery .thumb'),  
            'filmstrip_arrow_left': $$('#feeds .participant_filters .filmstrip .arrow_left')[0],
            'filmstrip_arrow_right': $$('#feeds .participant_filters .filmstrip .arrow_right')[0],
            'clear_selection_link': $$('#feeds .participant_filters .link_clear_selection')[0],
            'results': $$('#feeds .results')[0],
            'feedback': $$('#feeds .results .feedback')[0],
            'feedback_header': $$('#feeds .results .feedback h2')[0],
            'feedback_throbber': $$('#feeds .results .feedback .throbber')[0],
            'tweets': $$('#feeds .results .tweets'),
            'more_results_link': $$('#feeds .results .pagination .link_more_results')[0]  
        };
        
        this.galleryTween = new Fx.Tween(this.element['filmstrip_gallery'], {
           'onComplete':this.updateArrows.bind(this) 
        });
    },
    
    /**
     * Check if URL contains participant name (highlight thumbnail on page load)
     */
    checkURLFilter: function() {
        if (this.participant_name) {
            var thumbnails = this.element['filmstrip_thumbnails'];
            var numThumbs = thumbnails.length;
            for (var i=0; i < numThumbs; i++) {
                var image = thumbnails[i];
                if (image.alt == this.participant_name) {
                    this.selectedImageCurrent = image;
                    break;
                };
            };
        };
    },
    
    /**
     * Conduct calculations for filmstrip
     */
    filmstripCalculations: function() {
        var viewport = this.element['filmstrip_viewport'];
        var gallery = this.element['filmstrip_gallery'];
        var thumbnails = this.element['filmstrip_thumbnails'];
        
        if (thumbnails.length != 0) {
            var image = thumbnails[0];
            var displacement = parseInt(image.getStyle('border-left-width')) +
            parseInt(image.getStyle('width')) +
            parseInt(image.getStyle('border-right-width')) +
            parseInt(image.getStyle('margin-right')) +
            parseInt(image.getStyle('padding-right')) +
            parseInt(image.getStyle('padding-left'));
            
            this.calculateGalleryWidth(displacement, viewport, gallery, thumbnails);
            this.calculateScrollLength(displacement, viewport, gallery, thumbnails);
        }
    },
    
    /**
     * Calculate and set the width of the gallery
     */
    calculateGalleryWidth: function(displacement, viewport, gallery, thumbnails) {
        gallery.setStyle('width',(thumbnails.length*displacement) + 'px');
    },
    
    /**
     * Calculate the amount of pixels the filmstrip should scroll by
     */
    calculateScrollLength: function(displacement, viewport, gallery, thumbnails) {
        var image = thumbnails[0];
        var numImages = thumbnails.length;
        var numDisplacedImages = Math.floor((viewport.offsetWidth + parseInt(image.getStyle('margin-right')))/displacement);
        
        this.scrollLengthFull =  numDisplacedImages*displacement;
        this.scrollPartial = ((numImages%numDisplacedImages)*displacement - parseInt(image.getStyle('margin-right'))) - (viewport.offsetWidth - this.scrollLengthFull);
        this.scrollMaxPosition = ((Math.floor(numImages/numDisplacedImages)-1)*this.scrollLengthFull) + this.scrollPartial;
        
        if (parseInt(gallery.getStyle('width')) <= this.scrollLengthFull) {
            this.element['filmstrip_arrow_left'].setStyle('visibility','hidden');
            this.element['filmstrip_arrow_right'].setStyle('visibility','hidden');
        };
        
        if (this.scrollMaxPosition < 0) {
            this.scrollMaxPosition  = 0;
        };
    },
    
    /**
     * Attach events to components in filter template
     */
    attachEvents: function() {
        this.addEvent_SearchFilters();
        this.addEvent_GalleryImages();
        this.addEvent_MoveGalleryLeft();
        this.addEvent_MoveGalleryRight();
        this.addEvent_MoreResultsLink();
        this.addEvent_ClearSelectionLink();
        this.addEvent_AJAXRefresh();
        this.addEvent_FilmstripAutoplay();
    },
    
    /**
     * Add event to filter feeds by search parameter (e.g. About, From)
     */
    addEvent_SearchFilters: function() {
        var searchFilters = this.element['search_filters']; 
        
        searchFilters.getChildren('a').each(function(link){
            link.addEvent('click', function(event){
                event.stop();
                clearInterval(this.AJAXRefreshInterval);
                clearInterval(this.FilmstripAutoplayInterval);
                
                new Request.HTML({
                    url: link.href,
                    data: 'only_feeds=true',
                    method: 'get',
                    onSuccess: this.processSuccess_SearchFilters.bind(this),
                    onError: this.processError.bind(this)
                }).send();
            }.bind(this));
        }.bind(this));
    },
    
    /**
     * Add events to filter by participant and add hover states to thumbnails
     */
    addEvent_GalleryImages: function() {
        var thumbnails = this.element['filmstrip_thumbnails'];
        var clearSelectionLink = this.element['clear_selection_link'];
        
        thumbnails.each(function(image){
            image.addEvent('click', function(event){
                event.stop();
                clearInterval(this.AJAXRefreshInterval);
                clearInterval(this.FilmstripAutoplayInterval);
                
                var requestURL = '';
                var filterText = '';
                var searchFilterText = '';
                
                if (this.selectedImageCurrent == image) {
                    this.selectedImageCurrent.className = 'thumb';
                    this.selectedImageCurrent = null;
                    this.selectedImageNext = null;
                    
                    clearSelectionLink.className = 'link_clear_selection hide';
                    requestURL = clearSelectionLink.href;
                    filterText = this.page_name;
                    searchFilterText = decodeURIComponent(requestURL.substring(requestURL.indexOf('search_filter=')+14,requestURL.length));
                } 
                else {
                    if (!this.selectedImageCurrent) {
                        this.selectedImageCurrent = image;
                        this.selectedImageCurrent.className = 'thumb selected';
                    } 
                    else {
                        this.selectedImageNext = image;
                        this.selectedImageNext.className = 'thumb selected';
                        this.selectedImageCurrent.className = 'thumb';
                        this.selectedImageCurrent = this.selectedImageNext;
                    };
                    
                    clearSelectionLink.className = 'link_clear_selection show';
                    requestURL = image.getParent().href;
                    filterText = decodeURIComponent(requestURL.substring(requestURL.indexOf('filter=')+7,requestURL.indexOf('&')));
                    searchFilterText = decodeURIComponent(requestURL.substring(requestURL.indexOf('search_filter=')+14,requestURL.length));
                };
                
                this.toggleThrobber(filterText, searchFilterText);
                
                var request = new Request.HTML({
                    url: requestURL,
                    data: 'only_results=true',
                    method: 'get',
                    onSuccess: this.processSuccess_GalleryImage.bind(this),
                    onFailure: this.processError.bind(this),
                    onComplete: this.toggleThrobber.bind(this, filterText, searchFilterText)
                });
                
                (function(){
                    request.send();
                }).delay(1550, this); //artificial delay so the throbber shows (should really only show after some time)
            }.bind(this));
            
            image.addEvent('mouseover', function(event){
                event.stop();
                if (this.selectedImageCurrent != image) {
                    image.className = 'thumb hover';
                }
            }.bind(this));
            
            image.addEvent('mouseout', function(event){
                event.stop();
                if (this.selectedImageCurrent != image) {
                    image.className = 'thumb';
                }
            }.bind(this));
        }.bind(this));
    },
    
    /**
     * Move gallery to the left
     */
    addEvent_MoveGalleryLeft: function() {
        var gallery = this.element['filmstrip_gallery'];
        var rightArrow = this.element['filmstrip_arrow_right'];
        
        rightArrow.addEvent('mousedown', function(event) {
            if (rightArrow.active) {
                rightArrow.addClass('active');
            }
        });
        rightArrow.addEvent('mouseup', function(event) {
            rightArrow.removeClass('active');
        });
        rightArrow.addEvent('click', function(event) {
            event.stop();
            clearInterval(this.FilmstripAutoplayInterval);
            
            var currentPosition = parseInt(gallery.getStyle('left'));
            var newPosition = currentPosition - this.scrollLengthFull;
            
            if (this.scrollMaxPosition != 0) {
                if ((newPosition <= -this.scrollMaxPosition) && (currentPosition != -this.scrollMaxPosition)) {
                    this.galleryTween.start('left', -this.scrollMaxPosition);
                }                
                else if (newPosition > -this.scrollMaxPosition) {
                    this.galleryTween.start('left', newPosition);
                }
                
            };
        }.bind(this));
    },
    
    /**
     * Move gallery to the right
     */
    addEvent_MoveGalleryRight: function() {
        var gallery = this.element['filmstrip_gallery'];
        var leftArrow = this.element['filmstrip_arrow_left'];
        
        leftArrow.addEvent('mousedown', function(event) {
            if (leftArrow.active) {
                leftArrow.addClass('active');
            }
        });
        leftArrow.addEvent('mouseup', function(event) {
            leftArrow.removeClass('active');
        });
        leftArrow.addEvent('click', function(event) {
            event.stop();
            clearInterval(this.FilmstripAutoplayInterval);
            
            var currentPosition = parseInt(gallery.getStyle('left'));
            var newPosition = currentPosition + this.scrollLengthFull;
            
            if (this.scrollMaxPosition != 0) {
                if ((newPosition >= 0) && (currentPosition != 0)) {
                    this.galleryTween.start('left', 0);
                }
                else if (newPosition < 0) {
                    this.galleryTween.start('left', newPosition);
                }
            }
        }.bind(this));
    },
    
    /**
     * Show more results for current filter
     */
    addEvent_MoreResultsLink: function() {
        var lastTweetSet = this.element['tweets'][this.element['tweets'].length-1];
        var tweetListings = lastTweetSet.getChildren('li');
        var moreResultsLink = this.element['more_results_link'];
        
        if (moreResultsLink) {
            moreResultsLink.addEvent('click', function(event){
                event.stop();
                var lastTweet = tweetListings[tweetListings.length-1];
                if (lastTweet.id) {
                    new Request.HTML({
                        url: moreResultsLink.href,
                        data: 'only_results=true&last_id=' + lastTweet.id,
                        method: 'get',
                        onSuccess: this.processSuccess_MoreResultsLink.bind(this),
                        onError: this.processError.bind(this)
                    }).send(); 
                }
            }.bind(this));
        };
    },
    
    /**
     * Remove filters and show default feed
     */
    addEvent_ClearSelectionLink: function() {
        var clearSelectionLink = this.element['clear_selection_link'];
        
        clearSelectionLink.addEvent('click', function(event){
            event.stop();
            clearInterval(this.AJAXRefreshInterval);
            clearInterval(this.FilmstripAutoplayInterval);
            
            this.selectedImageCurrent.className = 'thumb';
            this.selectedImageCurrent = null;
            this.selectedImageNext = null;
            
            clearSelectionLink.className = 'link_clear_selection hide';
            
            var clearSelectionLinkHREF = clearSelectionLink.href;
            var searchFilterText = decodeURIComponent(clearSelectionLinkHREF.substring(clearSelectionLinkHREF.indexOf('search_filter=')+14,clearSelectionLinkHREF.length));
            
            this.toggleThrobber.call(this, this.page_name, searchFilterText);
            
            var request = new Request.HTML({
                url: clearSelectionLink.href,
                data: 'only_results=true',
                method: 'get',
                onSuccess: this.processSuccess_ClearSelectionLink.bind(this),
                onError: this.processError.bind(this),
                onComplete: this.toggleThrobber.bind(this, this.page_name, searchFilterText)
            });
            (function(){
                request.send();
            }).delay(1550, this); //artificial delay so the throbber shows (should really only show after some time)
        }.bind(this));
    },
    
    /**
     * Add in new feeds via AJAX refresh
     */
    addEvent_AJAXRefresh: function() {
        
        if (this.AJAXRefreshInterval) {
            clearInterval(this.AJAXRefreshInterval);
        };
        
        var clearSelectionLink = this.element['clear_selection_link'];
        var firstTweetSet = this.element['tweets'][0];
        var tweetListings = firstTweetSet.getChildren('li');
        var interval = 30*1000;
        
        this.AJAXRefreshInterval = setInterval(function() {
            var firstTweet = tweetListings[0];
            if (firstTweet.id) {
                var requestURL = clearSelectionLink.href;
                if (this.selectedImageCurrent) {
                    requestURL = this.selectedImageCurrent.getParent().href;
                }
                new Request.HTML({
                    url: requestURL,
                    data: 'only_results=true&page=-1&first_id=' + firstTweet.id,
                    method: 'get',
                    onSuccess: this.processSuccess_AJAXRefresh.bind(this),
                    onFailure: this.processError.bind(this)
                }).send(); 
            }
        }.bind(this),interval)
    },
    
    /**
     * Run filmstrip in autoplay mode
     */
    addEvent_FilmstripAutoplay: function() {
        
        if (this.FilmstripAutoplayInterval) {
            clearInterval(this.FilmstripAutoplayInterval);
        };
        
        var interval = 5*1000;
        var moveDirection = 'left';
        
        this.FilmstripAutoplayInterval = setInterval(function() {
            var gallery = this.element['filmstrip_gallery'];
            var currentPosition = parseInt(gallery.getStyle('left'));
            
            if (currentPosition == 0) {
                moveDirection = 'left';
            }
            else if (currentPosition == -this.scrollMaxPosition) {
                moveDirection = 'right';
            }
            
            if (moveDirection == 'left') {
                var newPosition = currentPosition - this.scrollLengthFull;
                
                if (this.scrollMaxPosition != 0) {
                    if ((newPosition <= -this.scrollMaxPosition) && (currentPosition != -this.scrollMaxPosition)) {
                        this.galleryTween.start('left', -this.scrollMaxPosition);
                    }                
                    else if (newPosition > -this.scrollMaxPosition) {
                        this.galleryTween.start('left', newPosition);
                    }
                    
                };
            } 
            else if (moveDirection == 'right') {
                var newPosition = currentPosition + this.scrollLengthFull;
                
                if (this.scrollMaxPosition != 0) {
                    if ((newPosition >= 0) && (currentPosition != 0)) {
                        this.galleryTween.start('left', 0);
                    }
                    else if (newPosition < 0) {
                        this.galleryTween.start('left', newPosition);
                    }
                };
            };

        }.bind(this),interval);
    },
    
    /**
     * Update the arrows to be active or depending on the position of the filmstrip
     */
    updateArrows: function() {
        var viewport = this.element['filmstrip_viewport'];
        var gallery = this.element['filmstrip_gallery'];
        var leftArrow = this.element['filmstrip_arrow_left'];
        var rightArrow = this.element['filmstrip_arrow_right'];
        
        //if there is more to go make this active, otherwise disable it
        var currentPosition = Math.abs(parseInt(gallery.getStyle('left')));
        var max = this.scrollMaxPosition;
        
        if (gallery.offsetWidth <= viewport.offsetWidth) {
            leftArrow.active = false;
            leftArrow.removeClass('more_participants');
            
            rightArrow.active = false;
            rightArrow.removeClass('more_participants');
        } else if (currentPosition == 0) {
            leftArrow.active = false;
            leftArrow.removeClass('more_participants');
            
            //all the way left, but see if there is more to the right
            if (currentPosition < max) {
                rightArrow.active = true;
                rightArrow.addClass('more_participants');
            }
        } else if (currentPosition == max) {
            leftArrow.active = true;
            leftArrow.addClass('more_participants');
            
            rightArrow.active = false;
            rightArrow.removeClass('more_participants');
        } else {
            rightArrow.active = true;
            rightArrow.addClass('more_participants');
            
            leftArrow.active = true;
            leftArrow.addClass('more_participants');
        };
    },
    
    /**
     * Toggle throbber when loading in feeds
     */
    toggleThrobber: function(filterText, searchFilterText){
        var feedbackHeader = this.element['feedback_header'];
        var feedbackThrobber = this.element['feedback_throbber'];
        var proposition = '';
        
        if (searchFilterText == 'about') {
            proposition = 'about';
        } else if (searchFilterText == 'from') {
            proposition = 'from';
        }
        
        feedbackHeader.innerHTML = "Twitter Buzz " + proposition + " " +filterText.replace('+',' ','g');
        
        if (feedbackThrobber.hasClass('hide')) {
            feedbackThrobber.className = 'throbber';
        } else {
            feedbackThrobber.className = 'throbber hide';
        }
    },

   /**
    * Callback function - Search filters
    */
    processSuccess_SearchFilters: function(responseTree, responseElements, responseHTML, responseJavaScript) {
        this.element['hub_left'].innerHTML = responseHTML;
        this.setupWidget();
    },
    
   /**
    * Callback function - Gallery images
    */
    processSuccess_GalleryImage: function(responseTree, responseElements, responseHTML, responseJavaScript) {
        this.element['results'].innerHTML = responseHTML;
        this.setupElements();
        this.addEvent_MoreResultsLink();
        this.addEvent_AJAXRefresh();
    },
    
   /**
    * Callback function - 'Clear Selection' link
    */
    processSuccess_ClearSelectionLink: function(responseTree, responseElements, responseHTML, responseJavaScript) {
        this.element['results'].innerHTML = responseHTML;
        this.setupElements();
        this.addEvent_MoreResultsLink();
        this.addEvent_AJAXRefresh();
    },
    
   /**
    * Callback function - 'More' link
    */
    processSuccess_MoreResultsLink: function(responseTree, responseElements, responseHTML, responseJavaScript) {
        var results = this.element['results'];
        var moreResultsLink = this.element['more_results_link'];
        
        // Remove old 'More' link
        var oldMoreLinkContainer = moreResultsLink.getParent();
        oldMoreLinkContainer.getParent().removeChild(oldMoreLinkContainer);
        
        // Create HTML elements from raw HTML returned from AJAX request
        var el = new Element('div');
        el.innerHTML = responseHTML;
        
        // Add search results to page (will always return at least one element (e.g. no more feeds message))
        var newTweets = el.getElement('ul.tweets');
        if (newTweets) {
            var newListings = newTweets.getElements('li');
            var numListings = newListings.length;
            if (numListings > 0) {
                newTweets.inject(results);
            }
        };
        
        // Add add 'More' link to page
        var newMoreLinkContainer = el.getElement('div.pagination');
        if (newMoreLinkContainer) {
            newMoreLinkContainer.inject(results);
        };
        
        // Update click event for 'More' link to reflect new page number
        this.setupElements();
        this.addEvent_MoreResultsLink();
    },
    
   /**
    * Callback function - AJAX refresh
    */
    processSuccess_AJAXRefresh: function(responseTree, responseElements, responseHTML, responseJavaScript){
        var feedback = this.element['feedback'];
        
        // Create HTML elements from raw HTML returned from AJAX request
        var el = new Element('div');
        el.innerHTML = responseHTML;
        
        // Add search results to page
        var newTweets = el.getElement('ul.tweets');
        if (newTweets) {
            var newListings = newTweets.getElements('li');
            var numListings = newListings.length;
            if (numListings > 0) {
                newTweets.inject(feedback, 'after');
                var startColor = '#FFFACD';
                var endColor = '#FFFFFF';
                var time = 2500;
                newListings.each(function(listing) {
                    listing.setStyle('background-color',startColor);
                    listing.highlight(startColor, endColor, [{duration: time}]);
                    listing.getElement('a.reply').tween('background-color', startColor, endColor, [{duration: time}]);
                    listing.getElement('a.retweet').tween('background-color', startColor, endColor, [{duration: time}]);
                });
            }
        };
    },
    
    /**
    * Error callback functions when requesting feeds via AJAX
    */
    processError: function(responseText) {
        //update the modal
    },
    
    initialize: function(conf){
        if ($(conf['id'])) {
            this.conf = conf;
            this.setupWidget();
        } else {
            return null;
        }
    }
});

/**
 * @author roel
 * Code for filmstrip widget
 */
var FilmstripWidget = new Class({
    
    /**
     * Conduct calculations for filmstrip
     */
    filmstripCalculations: function() {
        var frame = this.frames[0];
        var displacement = frame.offsetWidth + parseInt(frame.getStyle('margin-right'));
        this.calculateGalleryWidth(frame,displacement);
        this.calculateScrollLength(frame,displacement);
    },
    
    /**
     * Calculate and set the width of the gallery
     */
    calculateGalleryWidth: function(frame,displacement) {
        this.gallery.setStyle('width',(this.frames.length*displacement) - parseInt(frame.getStyle('margin-right')) + 'px');
    },
    
    /**
     * Calculate the amount of pixels the filmstrip should scroll by
     */
    calculateScrollLength: function(frame,displacement) {
        var numFrames = this.frames.length;
        var numDisplacedFrames = Math.floor((this.viewport.offsetWidth + parseInt(frame.getStyle('margin-right')))/displacement);
        this.scrollLengthFull =  numDisplacedFrames*displacement;
        this.scrollLengthPartial = ((numFrames%numDisplacedFrames) * displacement - parseInt(frame.getStyle('margin-right'))) - (this.viewport.offsetWidth - this.scrollLengthFull);
        this.scrollMaxPosition = ((Math.floor(numFrames/numDisplacedFrames)-1)*this.scrollLengthFull) + this.scrollLengthPartial;
        
        if (this.scrollMaxPosition < 0) {
            this.scrollMaxPosition = 0;
        }
    },
    
    /**
     * Attach events to filmstrip
     */
    attachEvents: function() {
        this.addEvent_MoveGalleryLeft();
        this.addEvent_MoveGalleryRight();
    },
    
    addEvent_MoveGalleryLeft: function() {
        this.arrowRight.addEvent('mousedown', function(event) {
            if (this.active) {
                $(this).addClass('active');
            }
        });
        this.arrowRight.addEvent('mouseup', function(event) {
            $(this).removeClass('active');
        });
        this.arrowRight.addEvent('click', function(event) {
            event.stop();
            var currentPosition = parseInt(this.gallery.getStyle('left'));
            var newPosition = currentPosition - this.scrollLengthFull;
            
            if (this.scrollMaxPosition != 0) {
                if ((newPosition <= -this.scrollMaxPosition) && (currentPosition != -this.scrollMaxPosition)) {
                    this.galleryTween.start('left', -this.scrollMaxPosition);
                }                
                else if (newPosition > -this.scrollMaxPosition) {
                    this.galleryTween.start('left', newPosition);
                }
                
            }
        }.bind(this));
    },
    
    addEvent_MoveGalleryRight: function() {
        this.arrowLeft.addEvent('mousedown', function(event) {
            if (this.active) {
                $(this).addClass('active');
            }
        });
        this.arrowLeft.addEvent('mouseup', function(event) {
            $(this).removeClass('active');
        });
        this.arrowLeft.addEvent('click', function(event) {
            event.stop();
            var currentPosition = parseInt(this.gallery.getStyle('left'));
            var newPosition = currentPosition + this.scrollLengthFull;
            
            if (this.scrollMaxPosition != 0) {
                if ((newPosition >= 0) && (currentPosition != 0)) {
                    this.galleryTween.start('left', 0);
                }
                else if (newPosition < 0) {
                    this.galleryTween.start('left', newPosition);
                }
            }
        }.bind(this));
    },
    
    updateArrows: function() {
        var currentPosition = Math.abs(parseInt(this.gallery.getStyle('left')));
        var max = this.scrollMaxPosition;
        
        right = this.arrowRight;
        left = this.arrowLeft;
        
        if (this.gallery.offsetWidth <= this.viewport.offsetWidth) {
            left.active = false;
            left.removeClass('more_participants');
            
            right.active = false;
            right.removeClass('more_participants');
        } else if (currentPosition == 0) {
            left.active = false;
            left.removeClass('more_participants');
            
            right.active = true;
            right.addClass('more_participants');
        } else if (currentPosition == max) {
            left.active = true;
            left.addClass('more_participants');
            
            right.active = false;
            right.removeClass('more_participants');
        } else {
            right.active;
            right.addClass('more_participants');
            
            left.active;
            left.addClass('more_participants');
        }
    },
        
    initialize: function(conf){
        if ($(conf['id'])) {
            var id = conf['id'];
            
            this.filmstrip = $(id);
            this.arrowLeft = $$('#' + id + ' .arrow_left')[0];
            this.arrowRight = $$('#' + id + ' .arrow_right')[0];
            this.viewport = $$('#' + id + ' .viewport')[0];
            this.gallery = $$('#' + id + ' .gallery')[0];
            this.frames = $$('#' + id + ' .frame');  
            
            this.scrollLengthFull = 0;
            this.scrollLengthPartial = 0;
            this.scrollMaxPosition = 0;  
            
            this.galleryTween = new Fx.Tween(this.gallery, {
               'onComplete':this.updateArrows.bind(this) 
            });
            
            this.filmstripCalculations();
            this.attachEvents(); 
            this.updateArrows();
        } else {
            return null;
        }
    }
});

var FollowWidget = new Class({
    
    applyFilter: function(filter) {
        this.updateActiveTab(filter);
        this.filterParticipants(filter);
    },
    
    filterParticipants: function(filter) {
        var filters = filter.getAttribute('class').trim().split(" ");
        filters.erase('last');
        
        //go through the participants showing those that match
        //the filter and hide those that don't.  A match
        //is determined by the participant having a class that
        //matches one in the current tab
        var last_participant = null;
        this.follow_participants.getElements('li').each(function(p) {
            var p_class = p.getAttribute('class').trim().split(" ");
            if(p_class.intersect(filters).length) {
                p.removeClass('hidden');
                p.removeClass('last');
                last_participant = p;
            } else {
                p.addClass('hidden');
            }
        });
        //if there were any participants under this filter, make
        //the last one have the "last" css class
        if(last_participant) {
            last_participant.addClass('last');
        }
    },
    
    filterSubLists: function(filter) {
        var filters = filter.getAttribute('class').trim().split(" ");
        
        //go through the participants showing those that match
        //the filter and hide those that don't.  A match
        //is determined by the participant having a class that
        //matches one in the current tab
        this.sublists.each((function(list) {
            var list_class = list.getAttribute('class').trim().split(" ");
            if(list_class.intersect(filters).length) {
                list.removeClass('hidden');
                //show the participants for the defalt filter of this list
                this.applyFilter(list.getElement(".default"));
            } else {
                list.addClass('hidden');
            }
        }).bind(this));
    },
    
    updateActiveTab: function(filter){
        filter.parentNode.getChildren().each(function(li) {
            if (filter != li) {
                li.removeClass('active');
            } else {
                filter.addClass('active');
            }
        });
    },
    
    attachSubListEvents: function() {
        this.sublists.each((function(list) {
            list.getElements('li').each((function(tab) {
                tab.addEvent('click', (function(e) {
                    e.stop();
                    this.applyFilter(tab);
                }).bind(this));
            }).bind(this));
        }).bind(this));
    },
    
    attachTabEvents: function() {
        this.tabs.getElements('li').each((function(tab) {
            tab.addEvent('click', function(e) {
                e.stop();
                this.applyFilter(tab);
                this.filterSubLists(tab);
            }.bindWithEvent(this));
        }).bind(this));
    },
    
    initialize: function(conf) {
        if($(conf['filter_tabs'])) {
            this.tabs = $(conf['filter_tabs']);
            this.follow_participants = $(conf['follow_participants']);
            this.sublists = $$(conf['follow_subfilters']);
            this.attachTabEvents();
            this.attachSubListEvents();
        }
    }
});



/**
 * @author Pete
 * The main tweetswirl object that provides helper functions to the rest
 * of the widgets on the site.
 */
var TweetSwirl = {
    
    openOauthWindow: function(e) {
        //stop the event, if there is one
        if (e) {
            e.stop();
        }
        var w_size = window.getSize();
        var oauth_w = {
            'w': 815,
            'h': 355,
            'top': w_size.y / 2 - (335 / 2),
            'left': w_size.x / 2 - (815 / 2)
        };
        var oauth_win = window.open($("signin_button").href, 'oauth', 'height=' + oauth_w.h + ',width=' + oauth_w.w + ',left=' + oauth_w.left + ',top=' + oauth_w.top);
        function check() {
            if(oauth_win.closed) {
                //stop the timer
                $clear(TweetSwirl.oauthTimer);
                //refresh the login area (make an ajax request to get it)
                TweetSwirl.updateSigninStatus();
            } else {
                TweetSwirl.oauthTimer = check.delay(50);
            }
        };
        check();
    },
    
    updateSigninStatus: function(e, logout) {
        //stop the event, if there is one
        if (e) {
            e.stop();
        }
        var logout = logout || false;
        //if it time to log out we need to set
        //the global variable so all the widgets will work
        var auth_check = new Request({
            onSuccess: function(text, xml) {
                if(logout) {
                    //log out if this is for logging out
                    window.TS_auth = false;
                } else {
                    window.TS_auth = true;
                }
            }
        }).get('/common/auth_check');
        var auth_status = new Request.HTML({
            onSuccess: function(tree, elements, html, js) {
                $("signin").innerHTML = html;
                setupSigninEvents();
            }
        }).get('/common/auth_status?logout='+logout);
    },
    
    /**
     * Create a modal with some given content.  This should
     * follow some generic modal design/layout, and a className
     * can be passed in to change the display.
     *
     * Returns the dom id of the modal.
     *
     * @param {Object} content - the header and content to be shown
     * @param {String} className - the css class name for the modal
     * @return {int} id - the dom id of the modal
     */
    createModal: function(content, className){
        var id = new Date().getTime().toString();
        var className = className || "modal";
        
        
        //main div
        var modal = new Element('div');
        modal.id = id;
        modal.className = className;
        
        //title
        var title = new Element('div');
        title.className = 'title';
        
        //content
        var content = new Element('div');
        content.className = 'content';
                
        content.title = content.title || '';
        content.content = content.content || '';
        
        title.innerHTML = content.title;
        content.innerHTML = content.content;
        
        modal.appendChild(title);
        modal.appendChild(content);
        
        //add it to the body
        modal.injectInside($$('body')[0]);
        
        var modalMask = new Element('div');
        modalMask.id = id + 'mask';
        modalMask.className = className || "modalmask"
        modalMask.injectInside($$('body')[0]);
        
        return id;
    },
    
    
    /**
     * Delete a modal from the dom.
     * @param {Object} id
     */
    deleteModal: function(id){
        $(id).destroy();
        $(id + 'mask').destroy();
    },
    
    /**
     * Instead of deleting and recreating modals a given modal
     * can have its content swapped out.
     * @param {Int} id - the id of the modal ot modify
     * @param {String} content - the new content
     * @param {String} className - the name of the class
     */
    resetModal: function(id, content, className) {
        var className = className || 'modal';
        $(id).className = className;
        
        TweetSwirl.setModalContent(id, content);
    },
    
    /**
     * Set the title and main content for the modal.
     * Can take either a string that's assumed to be used for
     * innerHTML or an element, which is assume to be the new
     * content.  Either way a title can be passed in.
     * @param {Object} id
     * @param {Object} content
     */
    setModalContent: function(id, content) {
        content.title = content.title || '';
        content.content = content.content || '';
        
        $(id).getElements('.title')[0].innerHTML = content.title;
        if ($type(content.content) == 'element') {
            $(id).getElements('.content')[0].empty().appendChild(content.content);
        } else {
            $(id).getElements('.content')[0].innerHTML = content.content;
        }
    },
    
    /**
     * Shows the modal by setting a css class.
     * @param {Int} id - the id of ht emodal to show
     */
    showModal: function(id) {
        $(id).className = 'modal modal_show';
        $(id + 'mask').className = 'modalmask modalmask_show';
    },
    
    /**
     * Hide the modal by setting a css class.
     * @param {Int} id - the id of the modal to hide
     */
    hideModal: function(id) {
        $(id).className = 'modal modal_hide';
        $(id + 'mask').className = 'modalmask modalmask_hide';
    },
    
    /**
     * Hide the modal by setting a css class to be
     * display none.
     * @param {Int} id - the id of the modal to disappear
     */
    disappearModal: function(id) {
        $(id).className = 'modal modal_disappear';
        $(id + 'mask').className = 'modalmask modalmask_dissappear';
    },
    
    /**
     * Show the modal by setting a css class to be
     * display block.
     * @param {Int} id - the id of the modal to appear
     */
    appearModal: function(id) {
        $(id).className = 'modal modal_appear';
        $(id + 'mask').className = 'modalmask modalmask_appear';
    },
    
    /**
     * Position the modal in the middle of the browser window
     * @param {Int} id - the id of the modal to position
     */
    positionModal: function(id){
        var modal = $(id);
        //use mootools dimensions
        var browserDimensions = [window.innerWidth, window.innerHeight];

        modal.style.top = (window.getSize().y/2) - (modal.getSize().y/2) + window.getScroll().y + 'px';
        modal.style.left = (window.getSize().x/2) - (modal.getSize().x/2) + window.getScroll().x + 'px';
    },
    
    /**
     * Reset the modal and show it to user with the new content.
     * @param {Int} id - the id of the modal to update
     * @param {String} content - the new content to display
     */
    updateModal: function(id, content){
        TweetSwirl.hideModal(id);
        TweetSwirl.resetModal(id,content);
        TweetSwirl.appearModal(id);
        TweetSwirl.positionModal(id);
        TweetSwirl.showModal(id);
    }
};

/**
 * Add event to signin to use a popup window
 */
function setupSigninEvents() {
    if ($("signin_button")) {
        $("signin_button").addEvent('click', TweetSwirl.openOauthWindow);
    } else if ($("signout_button")) {
        $("signout_button").addEvent('click', TweetSwirl.updateSigninStatus.bindWithEvent(this, true));
    }
}
setupSigninEvents();

/**
 * Javascript to make suckerfish work in non-capable browsers
 */
sfHover = function() {
    var sfEls = document.getElements(".menu_nav").getElements("li");
    for (var i=0; i<sfEls.length; i++) {
        sfEls[i].onmouseover=function() {
            this.className+=" sfhover";
        }
        sfEls[i].onmouseout=function() {
            this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
        }
    }
}
if (window.attachEvent) window.attachEvent("onload", sfHover);





/* here for now*/
/* taken from http://www.esteak.net/plugin/Array_Extras*/
Array.implement({
    
    /**
     * Creates an intersection of the current system and the given one.
     * Returns as new array.
     * @param Array array
     */
    intersect: function(other) {
        var cpy = this.slice();
        this.each(function(el) {
            if (other.indexOf(el) < 0) {            
                cpy.splice(cpy.indexOf(el), 1);
            }
        }, this);
        return cpy;
    },
    
    /**
     * Returns the symmetric difference between this array and the given one.
     * Means the items both arrays include are removed from both and then both are combined.
     */
    differentiate: function(other) {
        var src = this.slice();
        var cmp = other.slice();
        other.each(function(elem) {
            if (src.indexOf(elem) > -1) {
                // remove from both
                src.splice(src.indexOf(elem), 1);
                cmp.splice(cmp.indexOf(elem), 1);
            }
        }, this);
        // combine remaining items
        return src.combine(cmp);
    },
    
    /**
     * Returns the given number of elements from the array starting 
     * at the given index.
     * @param int start index
     * @param int number of elements to return
     */
    getRange: function(start, elements) {
        var res = [];
        var j = 0;
        var upper = start + elements > this.length ? this.length : start + elements;
        if (start >= 0) {
            for (var i = start; i < upper; i++) {
                res[j++] = this[i];
            }
        }
        return res;
    }

});