/*****************************************************************************
 * UTILITY FUNCTIONS
 *****************************************************************************/
function log(v) {
    if (typeof(console) == 'undefined') { return; }
    if (typeof(console.debug) == 'function') { console.debug(v); }
    else if (typeof(console.log) == 'function') { console.log(v); }
}


$.extend(String.prototype,
{
    ucFirst: function() { return this.substr(0, 1).toUpperCase() + this.substr(1); },

    trim: function(delim)
    {
        if (typeof(delim) == 'undefined') { delim = '\\s'; }
        return this.replace(new RegExp('^(' + delim + '*)|(' + delim + '*)$', 'g'), '');
    },

    ellipse: function(maxLen)
    {
        return (this.length >= maxLen) ? (this.substr(0, maxLen - 3) + '...') : this;
    },

    strpadLeft: function(len, str)
    {
        var out = this;
        for (var out = this; out.length < len; out = str + out);
        return out;
    }
});

var Utils =
{
    /**
     *
     */
    arrayMerge: function(/* objects */)
    {
        var retObj = {};
        for (var i = 0; i < arguments.length; i++) {
            for (var ii in arguments[i]) { 
                retObj[ii] = arguments[i][ii];
            }
        }
        return retObj;
    },

    /**
     *
     */
    navigateViaPost: function(url, params)
    {
        var action = (typeof(url) == 'string') ? ('action="' + url + '"') : '';
        var html = '<form ' + action + '" method="POST" style="display:none;">';
        for (var i in params) {
            html += '<input type="hidden" name="' + i
                                      +'" value="' + params[i] + '" />';
        }
        html += '</form>';

        var form = $(html);
        $('body').append(form);
        form.submit();
    },

    /**
     *
     */
    reloadWithParam: function(paramName, value)
    {
        var parts = document.location.href.match(/([^\?]+)\??(.*)$/);

        var regex = new Regexp('(page|' + paramName + ')=[^&]*&?');

        if ((typeof(parts[2]) != 'undefined') && parts[2]) {
            var q = parts[2].replace(regex, '');
        } else {
            var q = '';
        }

        document.location.href = parts[1]
                               + '?'
                               + q
                               + ((q == '' || q.substr(-1) == '&') ? '' : '&')
                               + 'sort=' + value;
    }
};
/*****************************************************************************
 * JQUERY EXTENSIONS
 *****************************************************************************/
(function($) {

    // we''re going to override val().  Keep a copy of the original function
    var parentValFunc = $.fn.val;

    // array of hint texts
    $.inputHints     = new Object;
    $.inputMaxLength = new Object;

    $.clearAllHints = function()
    {
        for (var i in $.inputHints) {
            var el = $('#' + i);
            el.handleHintFocus();

            if (el.attr('type') == 'password') {
                var substEl = $('#' + i + '_hintSubstitute');
                substEl.unbind('focus');
                substEl.remove();
                el.show();
            }
            delete $.inputHints[i];
        }
    }

    $.fn.extend({
        /**
         * setHint() - initialise the input hint.
         *
         * @param string hintText - text to be used when field is empty.
         */
        setHint: function(hintText)
        {
            var domId = this.attr('id');
            var exists = typeof($.inputHints[domId]) != 'undefined';
            // Store the Hint Text
            $.inputHints[this.attr('id')] = ' '+hintText;
            // Store maxlength so we can restore it when we clear it
            if (this.attr('maxlength') > 0){
               $.inputMaxLength[this.attr('id')] = this.attr('maxlength'); 
            } else {
               $.inputMaxLength[this.attr('id')] = "";
            }

            if (exists) {
                parentValFunc.call(this, '');
            } else {
                var me = this;
                this.bind('blur',  function() { me.handleHintBlur(); });
                this.bind('focus', function() { me.handleHintFocus(); });
            }

            this.handleHintBlur();
        },

        /**
         * showHint() - shows hint text
         */
        showHint: function()
        {
            this.addClass('inputHint');
            this.removeAttr("maxlength");
            return parentValFunc.call(this, $.inputHints[this.attr('id')]);
        },

        /**
         * hideHint() - hides hint text
         */
        hideHint: function()
        {
            this.removeClass('inputHint');
            if ($.inputMaxLength[this.attr('id')] != ""){
                this.attr("maxlength", $.inputMaxLength[this.attr('id')]);
            }
            parentValFunc.call(this, '');
        },

        showHintPassword: function()
        {
            if ($.browser.msie) { return; }

            var me = this;

            if ($.browser.msie) {
                var outerHtml = this.get(0).outerHTML;

                var clone = $(outerHtml.replace('type="password"', 'type="text"'));
            } else {
                var clone = this.clone().attr({'type'  : 'text'});
            }

            clone.attr({'id'    : this.attr('id') + '_hintSubstitute',
                        'style' : 'display:none;'})
                 .bind('focus', function() {
                          $(this).remove();
                          me.show();
                          me.get(0).focus();
                      })
                 .insertAfter(this)
                 .addClass('inputHint')
                 .removeAttr("maxlength");

            this.hide();
            clone.show();

            return parentValFunc.call(clone, $.inputHints[this.attr('id')]);
        },

        /**
         * handleHintBlur() - callback for blur of input field
         */
        handleHintBlur: function()
        {
            if (parentValFunc.call(this) === '') {
                if (this.attr('type') == 'password') {
                    this.showHintPassword();
                } else {
                    this.showHint();
                }
            }
        },

        /**
         * handleHintFocus() - callback for focus of input field
         */
        handleHintFocus: function()
        {
            if (parentValFunc.call(this) == $.inputHints[this.attr('id')]) {
                this.hideHint();
            }
        },

        /**
         * val() - overrides jquery val() prototype.  If we're getting the value
         *         we make sure not to return the hint text.  If we're setting
         *         the value, we make sure to show the hint text if necessary.
         */
        val: function(value) {

            var domId = this.attr('id');

            if (typeof($.inputHints[domId]) == 'undefined') {
                return parentValFunc.call(this, value);
            }

            if (typeof(value) != 'undefined') {          // setting
                if (value == '') {
                    return this.showHint(domId);
                } else { 
                    this.removeClass('inputHint');
                    return parentValFunc.call(this, value);
                }
            } else {                                     // getting
                // get the value
                var retVal = parentValFunc.call(this, value);

                return ($.inputHints[domId] == retVal) ? '' : retVal;
            }
        },

        /**
         *
         */
        selectByValue: function(value)
        {
            switch (this.length) { // call this function for multiple selects
                case 0: return this;
                case 1: break;
                default:
                    this.each(function() { $(this).selectByValue(value) });
                    return this;
            }

            if (this.length < 1 || this.get(0).tagName != 'SELECT') {
                return this;
            }

            var indexFound = -1;
            this.find('option').each(function(i) {
                if (value == this.value) {
                    indexFound = i;
                    return false;
                }
            });

            if (indexFound != -1) {
                this.get(0).selectedIndex = indexFound;
            }

            return this;
        },

        /**
         *
         */
        getSelectedOption: function()
        {
            if (this.get(0).tagName != 'SELECT') {
                return $([]);
            }

            return this.find('option').eq(this.get(0).selectedIndex);
        },

        /**
         *
         */
        replaceWith: function(elIn, speed, callback)
        {
            elIn = $(elIn);
            speed    = (typeof(speed)    == 'undefined') ? 'fast'        : speed;
            callback = (typeof(callback) != 'function')  ? function() {} : callback;
            var me = this;

            var doneFade = false;

            if (this.is(':visible')) {
                this.fadeOut(speed, function()
                {
                    if (!doneFade) {
                        if (!elIn.is(':visible')) {
                            elIn.fadeIn(speed, callback);
                        } else {
                            callback();
                        }

                        doneFade = true;
                    }
                });
            } else {
                if (!elIn.is(':visible')) {
                    elIn.fadeIn(speed, callback);
                } else {
                    callback();
                }
            }

            return this;
        },

        /**
         *
         */
        displayValue: function()
        {
            if (this.length == 0) {
                return null;
            }

            switch (this.get(0).tagName) {
                case 'DIV': case 'SPAN': case 'P': case 'A':
                    var func = $.fn.html;
                    break;
                case 'INPUT': case 'TEXTAREA':
                    var func = $.fn.val;
                    break;
                case 'IMG':
                    var func = function() {
                        var args = arguments;
                        [].unshift.call(args, 'src');
                        return this.attr.apply(this, args);
                    };
                    break;
                default:
                    return null;
            }

            return func.apply(this, arguments);
        },

        /**
         *
         */
        htmlAddition: function(value, attr)
        {
            if (typeof(attr) == 'undefined') { attr = 'innerHTML'; }
            this.attr(attr, (String)((Number)(this.attr(attr)) + value));
            return this;
        }
    });

})(jQuery);

/*****************************************************************************
 * JQMODAL IMPLEMENTATION
 ****************************************************************************/
var WurbbleJqm = 
{
    callbacks: {},

    /**
     *
     */
    getWinHeight: function() {
        if ( typeof( window.pageYOffset) == 'number' ) {
            return window.pageYOffset;
        } else if ( typeof (document.documentElement ) != 'undefined'
                && ( typeof(document.documentElement.scrollTop) != 'undefined') ) {
            return document.documentElement.scrollTop;
        } else if ( typeof( document.body ) != 'undefined'
                 && ( typeof(document.body.scrollTop) != 'undefined' ) ) {
            return document.body.scrollTop;
        }

        return 0;
    },

    /**
     *
     */
    handleShow: function(hash)
    {
        $('#content embed:visible').css('visibility', 'hidden')
                                   .attr('jqmHiding', 1);

        hash.w.fadeIn('fast', function()
        {
            hash.w.css({ 'top':(WurbbleJqm.getWinHeight() + 20) + 'px' });
            var hideCallback = WurbbleJqm.callbacks[hash.w.attr('id') + 'Show']();
            if (typeof(hideCallback) == 'function') {
                WurbbleJqm.callbacks[hash.w.attr('id') + 'AutoHide'] = hideCallback;
            }
            delete WurbbleJqm.callbacks[hash.w.attr('id') + 'Show'];
        });
    },

    /**
     *
     */
    handleHide: function(hash)
    {
        hash.w.fadeOut('fast', function()
        {
            $('#content embed[jqmHiding=1]').removeAttr('jqmHiding')
                                            .css('visibility', 'visible');
            var doNextCallback = WurbbleJqm.callbacks[hash.w.attr('id') + 'Hide']();

            delete WurbbleJqm.callbacks[hash.w.attr('id') + 'Hide'];
            if (typeof(WurbbleJqm.callbacks[hash.w.attr('id') + 'AutoHide']) == 'function') {
                if ((typeof(doNextCallback) == 'undefined') || (doNextCallback !== false)) {
                    WurbbleJqm.callbacks[hash.w.attr('id') + 'AutoHide']();
                }
                delete WurbbleJqm.callbacks[hash.w.attr('id') + 'AutoHide'];
            }

        });

        hash.o.remove();
    }
}

// Make jqmShow and jqmHide accept a callback and save them to global variables
$(['Show', 'Hide']).each(function()
{
    var showOrHide = this;
    var parentFunc = $.fn['jqm' + showOrHide];

    $.fn['jqm' + showOrHide] = function(callback)
    {
        callback = (typeof(callback) == 'function')
                 ? callback : function () {};

        WurbbleJqm.callbacks[this.attr('id') + showOrHide] = callback;

        return parentFunc.call(this);
    }
});

/*****************************************************************************
 * FLASH VIDEO
 *****************************************************************************/
var FlashVideo = 
{
    scrolling : {},

    onPlayerReady: function(player)
    {
        var matches;
        try {

            if (matches = player.id.match(/(.*)_wurbbleVideo(.*)_flashVideo/)) {
                var playerToShow = window.document[player.id];
                playerToShow.addModelListener('META',      'FlashVideo.handleMetadata');
                playerToShow.addModelListener('STATE',     'FlashVideo.handleState');
                playerToShow.addControllerListener('SEEK', 'FlashVideo.handleSeek');

                UserNameSlide(player.id).setFirstUserName(
                    $('#' + player.id).parents('div.ct-main-center:eq(0)')
                                      .find('div.selection > span.name-item:eq(0) > a.wurbbleUserName').html()
                );
            }
        } catch (e) { log(e); }
            /*
            var playerToShow = window.document[player.id];
            var matches = null;

            if (matches = player.id.match(/(.*)_wurbbleVideo(.*)_flashVideo/)) {
                FlashVideo.scrolling[player.id] = {'domId' : matches[1] + '_textOverlay' + matches[2] };

                playerToShow.addModelListener('STATE', 'FlashVideo.handleStateChange');
            }

        } catch(e) { log(e); }
        */
    },

    handleMetadata: function(data)
    {
        try {
            if (typeof(data.mergeflvComponentName) != 'undefined') {
                UserNameSlide(data.id).handleNewVideo(data.mergeflvComponentName);
            } else if (typeof(data.duration) != 'undefined') {
                UserNameSlide(data.id).start();
            }
        } catch (e) { log(e); }
    },

    handleState: function(data)
    {
        try {

            switch (data.newstate) {
                case 'PLAYING':
                    FlashVideo.wurbbleOverlayDisplay(data.id, false);

                    if (data.oldstate != 'BUFFERING' && data.oldstate != 'IDLE') {
                        UserNameSlide(data.id).resume();
                    }
                    break;
                case 'PAUSED': 
                    UserNameSlide(data.id).pause();
                    FlashVideo.wurbbleOverlayDisplay(data.id, true);
                    break;
                case 'IDLE':
                    UserNameSlide(data.id).stop();
                    // fallthrough
                case 'COMPLETED':
                    FlashVideo.wurbbleOverlayDisplay(data.id, true);
                    break;
                default:
            }
        } catch (e) { log(e); }
    },

    handleSeek: function(data)
    {
        try {
            UserNameSlide(data.id).stop();
        } catch (e) { log(e); }
    },

    wurbbleOverlayTimeout: 0,

    wurbbleOverlayDisplay: function(id, show)
    {
        var func = (show) ? 'fadeIn' : 'fadeOut';

        var matches = id.match(/(.*)_wurbbleVideo(Js|\d*)/);

        function doFade() {
            $('#' + matches[1] + matches[2] + '_topOverlay, #'
                  + matches[1] + matches[2] + '_bottomOverlay')[func]('fast');
        }

        if (show) {
            if (FlashVideo.wurbbleOverlayTimeout) {
                clearTimeout(FlashVideo.wurbbleOverlayTimeout);
                FlashVideo.wurbbleOverlayTimeout = 0;
            }
            doFade();
        } else {
            FlashVideo.wurbbleOverlayTimeout = setTimeout(doFade, 1000);
        }
    }
};

function UserNameSlide(domId)
{
    if (this == window) { // factory/accessor
        if (typeof(UserNameSlide._instances[domId]) == 'undefined') {
            UserNameSlide._instances[domId] = new UserNameSlide(domId);
        }
        return UserNameSlide._instances[domId]
    } else { // constructor
        this.domId = domId;
    }
}

$.extend(UserNameSlide,
{
    _instances : {}
});

UserNameSlide.prototype =
{
    firstUserName : 'Wurbble!',
    isFromLeft : true,
    sliding    : false,
    timeout    : 0,
    domId      : null,
    queue      : false,

    config: {
        frameWidth : 425, // must be the same as width of video frame
        width      : 300, // width of the name bar including borders
        pxMove     : 28,  // number of pixels to move bar every 25ms when animating
        hangTime   : 2000, // milliseconds after the bar has slid in before it retracts
        startDelay : 200 // milliseconds before the bar starts showing after video starts
    },

    setFirstUserName: function(firstUserName) { this.firstUserName = firstUserName; },

    animate: function()
    {
        var me = this;

        var el = $('#' + me.domId + '_userNameSlideContainer');
        var left = parseInt(el.css('left'));

        var keepSliding = false;

        if (this.sliding == 'in') {
            if (this.isFromLeft) {
                if (keepSliding = (left < 0)) {
                    el.css('left', Math.min(left + this.config.pxMove, 0) + 'px');
                }
            } else {
                if (keepSliding = (left > (this.config.frameWidth - this.config.width))) {
                    el.css('left', Math.max(left - this.config.pxMove,
                                            (this.config.frameWidth - this.config.width)) + 'px');
                }
            }

            if (keepSliding) {
                me.timeout = setTimeout(function() { me.animate() }, 25);
            } else {
                this.sliding = 'out';
                this.timeout = setTimeout(function() { me.animate() }, this.config.hangTime);
            }
        } else {
            if (this.isFromLeft) {
                if (keepSliding = (left > 0 - this.config.frameWidth)) {
                    el.css('left', (left - this.config.pxMove) + 'px');
                }
            } else {
                if (keepSliding = (left < this.config.width)) {
                    el.css('left', (left + this.config.pxMove) + 'px');
                }
            }

            if (keepSliding) {
                this.timeout = setTimeout(function() { me.animate() }, 25);
            } else {
                el.remove();
                this.sliding = false;
                if (this.queue) {
                    this.handleNewVideo(me.queue);
                }
            }
        }
    },

    handleNewVideo: function(userName)
    {
        var me = this;

        if (this.sliding) {
            this.queue = userName;
            this.sliding = 'out';
            clearTimeout(this.timeout);
            this.animate();
            return;
        }

        this.queue = false;

        this.isFromLeft = !this.isFromLeft;

        var style = 'width:' + this.config.width + 'px;'
                  + ((this.isFromLeft)
                      ? 'left:-' + this.config.width   + 'px;'
                      : 'left:'  + this.config.frameWidth + 'px;');

        var innerStyle = (this.isFromLeft) 
                       ?   'width:' + (this.config.width - 45) + 'px;'
                       :   'width:' + (this.config.width - 27) + 'px;';

        var textStyle = (this.isFromLeft) 
                    ? 'float:right;'
                    : 'float:left;';

        $(
            '<div id="' + this.domId + '_userNameSlideContainer"'
          + '     class="userNameSlideContainer" style="' + style + '">'
          + '  <div class="userNameSlide">'
          + ((this.isFromLeft) ? '' : '    <p class="userNameSlideLeft"></p>')
          + '    <div class="userNameSlideMain" style="' + innerStyle + '">'
          + '      <div class="textWrap" style="' + textStyle + '">'
          + '        <p class="nameTag">' + userName + '</p>'
          + '        <p class="shadow">' + userName + '</p>'
          + '      </div>'
          + '    </div>'
          + ((this.isFromLeft) ? '    <p class="userNameSlideRight"></p>' : '')
          + '  </p>'
          + '</div>'
        ).insertBefore('#' + this.domId);

        this.sliding = 'in';
        this.timeout = setTimeout(function() { me.animate(); }, this.config.startDelay);
    },

    start: function()
    {
        this.isFromLeft = true;
        this.handleNewVideo(this.firstUserName);
    },

    stop: function()
    {
        if (this.sliding) {
            this.sliding = false;
            clearTimeout(this.timeout);
            $('#' + this.domId + '_userNameSlideContainer').remove();
        }
    },

    pause:  function() { clearTimeout(this.timeout); },
    resume: function() { if (this.sliding) { this.animate(); } }

};


// function in "window.playerReady" gets called by SWFObject when a video has loaded
window.playerReady = FlashVideo.onPlayerReady;

var waitVideo = true;
$.fn.loadVideo = function(videoFilename, autoStart, isRtmp)
{
    var autostartParam = (autoStart) ? 1 : 0;
    if (!this.length) {
        throw new Error('Could not find element to render video into');
    }

    var el = this.get(0);
    //FlashVideo.loadingPlayer = el.id;

    var s1 = new SWFObject(Config.flashUrls.player,
                           el.id + '_flashVideo',
                           '425',
                           '328',
                           '9');

    s1.addParam('allowfullscreen','true');
    s1.addParam('allowscriptaccess','always');
    s1.addParam('wmode','transparent');

    s1.addVariable('repeat', 'list');
    s1.addVariable('autostart', autostartParam);

    if (isRtmp) {
        s1.addVariable('type',     'video');
        s1.addVariable('streamer', CommonConfig.VIDEO_RECORD_HOST);
        s1.addVariable('file',     videoFilename);
    } else {
        s1.addVariable('file', videoFilename);
    }
    s1.write(el);

    return this;
};
/*****************************************************************************
 * ALERT MESSAGES
 *****************************************************************************/
var AlertMessage =
{
    alertTimerID : 0,
    // Are we in an alert transition
    alertInTransition : false,
    // Are we in the process of hiding an alert?
    alertInTransitionHiding : false,
    // Hold queued alerts 
    alertsQueue : new Array(), 

    fadeSpeed: 'fast',

    /*
     * hideAlert() - hide an alert if its showing
     *
     * @param keepQueue = Default: false
     */
    hideAlert : function(keepQueue)
    {

        // Clear out the queue unless keepQueue
        if (typeof(keepQueue) == "undefined" || keepQueue == false){
            while (this.alertsQueue.length>0){
                this.alertsQueue.shift();
            }
        }

        // Dont Hide the Alert If Its Already In The Process Of Being Hidden
        if (this.alertInTransition || this.alertInTransitionHiding){
            // give it some time and try to process it later
            setTimeout(function(){ AlertMessage.hideAlert(true); }, 250);
            return;
        }

        // Clear teh Timer
        if (this.alertTimerID > 0){
            // Clear Old Timer
            clearTimeout(this.alertTimerID);
            this.alertTimerID = 0;
        }

        // Dont Hide An Alert That Isnt Showing
        if (!$("#alertSmall").is(":visible")){
            return;
        }

        // Dont Let any new alerts fire until we are done hiding
        this.alertInTransitionHiding = true;
 
        // Hide Alert
        $("#alertSmall").fadeOut(AlertMessage.fadeSpeed, function(){
            AlertMessage.alertInTransitionHiding = false;

            if (window.navigator.platform.match(/mac/i)) {
                $('iframe').not('[@scrolling=no]').css('overflow', 'visible');
            }

        });

    },

    /*
     * hideAlert() - hide an alert if its showing immediately (no fade out)
     */
    hideAlertNow : function()
    {
        while (this.alertsQueue.length>0) {
            AlertMessage.alertsQueue.shift();
        }

        AlertMessage.alertInTransitionHiding = false;
        AlertMessage.alertInTransition = false;

        // Clear teh Timer
        if (this.alertTimerID > 0){
            // Clear Old Timer
            clearTimeout(AlertMessage.alertTimerID);
            AlertMessage.alertTimerID = 0;
        }

        $('#alertSmall').hide();
        setTimeout("$('#alertSmall').hide();", 50);

        if (window.navigator.platform.match(/mac/i)) {
            $('iframe').not('[@scrolling=no]').css('overflow', 'visible');
        }
    },

    /*
     * triggerAlert() - Add a new alert to the queue. This is a public 
     *                  method we call to display an alert. 
     *
     * @param type = [ error, success, warn, loading ]
     * @param message = string message
     * @param timeout = milliseconds to show alert for
     */
    triggerAlert : function(type,message,timeout) 
    {

        // Default Hide Timeout = Never If Loading Message
        if ((typeof(timeout) == "undefined" || timeout == null) && type=="loading"){
            timeout = -1;
        }
        else if (typeof(timeout) == "undefined" || timeout == null){
            timeout = 5000;
        }

        // Add a new alert object to the queue
        this.alertsQueue.push({"type":type,"message":message,"timeout":timeout});

        // Go Display The Alert
        AlertMessage.doAlert();

    },

    /*
     *  doAlert() - Actually display a notice to teh user.
     */
    doAlert : function(){

        // Still working on showing/hiding a previous alert
        if (this.alertInTransition || this.alertInTransitionHiding){
            // give it some time and try to process it later
            setTimeout(function(){ AlertMessage.doAlert(); }, 250);
            return;
        }
        // No Alerts to display
        else if (this.alertsQueue.length==0){
            return;
        }

        // We are now in an alert transition
        this.alertInTransition = true;
   
        // No alert currently being displayed, get the next one from teh queue
        var alert = this.alertsQueue.shift();
        var message = alert.message;
        var type = alert.type;
        var timeout = alert.timeout;

        var typeTxt = '';
        var typeClass = '';

        // Minimum time an alert MUST be show for
        // before another alert can push it out
        var minDisplayTime = 1500; // 1.5 seconds 

        // What Type?
        switch (type)
        {
            case 'info':
            case 'notice':
          typeTxt = 'NOTICE:';
                typeClass = 'alertInfo'
                break;
            case 'success':
                typeTxt = 'SUCCESS!';
                typeClass = 'alertSuccess';
                break;
            case 'warn':
                typeTxt = 'WARNING!';
                typeClass = 'alertWarning';
                break;
            case 'error':
                typeTxt = 'ERROR!';
                typeClass = 'alertError';
                break;
            default:
                typeTxt = 'LOADING...';
                typeClass = 'alertLoading';
                minDisplayTime = 5; // .005 seconds 
                break;
        }

        // Set the Alert
        $('#alertSmallTypeTxt').text(typeTxt);
        $('#alertSmallTypeTxt').removeClass();
        $('#alertSmallTypeTxt').addClass(typeClass);
        $('#alertSmallTxt').html(message);
        // Need to set this here other wise another call to triggerAlert
        // before fadeIn/Out finishes will not be able kill this
        if (timeout > 0){
            this.alertTimerID = setTimeout(function(){ AlertMessage.hideAlert(true); },timeout);
        }

        if (window.navigator.platform.match(/mac/i)) {
            $('iframe').not('[@scrolling=no]').css('overflow', 'hidden');
        }
       
        // Show the alert
        $("#alertSmall").css("position","absolute");
        $("#alertSmall").css("top",$(window).scrollTop());
        $("#alertSmall").css("left",($(document).width()-980)/2);
        $("#alertSmall").css("width",980);
        $("#alertSmall").css("z-index",999999);
        $("#alertSmall").fadeIn(AlertMessage.fadeSpeed, function(){  
        // Show the alert for a minimum of minDisplayTime milliseconds
        setTimeout(function(){ AlertMessage.alertInTransition = false; }, minDisplayTime); 
        } );
    }
} // END AlertMessage

/*****************************************************************************
 * JS TEMPLATING
 *****************************************************************************/
function JsTemplate(templateName)
{
    if (templateName === null) { return; }

    if (typeof(JsTemplate.templates[templateName]) == 'undefined') {
        throw new Error('Could not find template ' + templateName);
    }

    if (this == window) {
        // FACTORY
        if (typeof(JsTemplate.templates[templateName].instance) == 'undefined') {
            JsTemplate.templates[templateName].instance = new JsTemplate(templateName);
        }
        return JsTemplate.templates[templateName].instance;
    } else {
        // CONSTRUCTOR
        this.templateName = templateName;
        $.extend(this, JsTemplate.templates[templateName]);
    }
}

$.extend(JsTemplate,
{
    templates: {},
    subTemplates: [],

    _render: function(data, html, iter)
    {
        var isVar = true;
        var newHtml = '';
 
        $(html.split(/\[%% | %%\]/)).each(function() {
            // "this" is the chunk of html
            if (isVar = !isVar) {
                if (this.indexOf('EXTRACT ') === 0) {
                    var matches = this.match(/^EXTRACT (\w+)$/);
                    
                    $.extend(data, data[matches[1]]);
                } else if (this.indexOf('ITERATE ') === 0) {
                    var matches = this.match(/^ITERATE (\w+) (\d+)$/);

                    if (  (typeof(data[matches[1]]) == 'undefined')
                       || (typeof(data[matches[1]].length) == 'undefined')) {

                        return true; // continue
                    }
 
                    $(data[matches[1]]).each(function(innerIter) { 
                        // "this" is an element of data
                        newHtml += JsTemplate._render(
                            Utils.arrayMerge(data, this),
                            JsTemplate.subTemplates[matches[2]],
                            iter + '_' + innerIter
                        );
                    });
                } else {
                    newHtml += (typeof(data[this]) != 'undefined')
                             ? data[this]
                             : '';
                }
            } else {
                newHtml += this.replace(/_jsTemplate_/g, iter);
            }
        });

        return newHtml;
    }
});

JsTemplate.prototype = 
{
    /**
     *
     */
    render: function(data, callback, prependOrEl)
    {
        var me = this;

        callback = (typeof(callback) == 'function')
                 ? callback
                 : function(html) { return html; }

        if (typeof(data.length) != 'undefined') {
            $(data).each(function() { me.render(this, callback); });
            return;
        }

        data = (typeof(data) == 'object')
             ? data
             : {};

        var iter = this.data.length;
        this.data.push(data);

        var html = callback(JsTemplate._render(data, this.html, iter));

        prependOrEl = (typeof(prependOrEl) == 'undefined') ? false : prependOrEl;

        if (prependOrEl === true) {
            $('#jsTemplatePlaceholder_' + this.templateName).parent().prepend(html);
        } else if (typeof(prependOrEl) == 'string') {
            $(prependOrEl).html(html);
        } else {
            $('#jsTemplatePlaceholder_' + this.templateName).before(html);
        }

        return iter;
    },

    /**
     *
     */
    count: function()
    {
        // filter out nulls
        return $.grep(this.data, function(el) { return el; }).length;
    },

    /**
     *
     */
    getData: function(iter)
    {
        switch (typeof(iter)) {
            case 'undefined':
                return this.data;
            case 'number':
                return this.data[iter];
            case 'string':
                return this.data[this._iter(iter)];
            default:
                return null;
        }
    },

    /**
     *
     */
    _iter: function(str)
    {
        if (typeof(str) != 'string') { return str; }

        var found = false;
        var el;
        var matches;

        switch (str) {
            case 'first':
                for (i = 0; i < this.data.length; i++) {
                    if (this.data[i] !== null) {
                        found = i;
                        break;
                    }
                }
                break;
            case 'last':
                for (i = data.length - 1; i > -1; i--) {
                    if (this.data[i] !== null) {
                        found = i;
                        break;
                    }
                }
                break;
            case 'firstInList':
                el = $('#jsTemplatePlaceholder_' + this.templateName).parent().children(':eq(0)');
                do {
                    if (el.attr('id') && (matches = el.attr('id').match(/\d+$/))) {
                        found = matches[0];
                        break;
                    }
                } while ((el = el.next()).length);
                break;
            case 'lastInList':
                el = $('#jsTemplatePlaceholder_' + this.templateName).prev();
                do {
                    if (el.attr('id') && (matches = el.attr('id').match(/\d+$/))) {
                        found = matches[0];
                        break;
                    }
                } while ((el = el.prev()).length);
                break;
            default:
        }
        return found;
    },

    /**
     *
     */
    remove: function(iter)
    {
        iter = this._iter(iter);

        this.data[iter] = null;

        // assumes all iterations' dom-ids end with the iteration number
        $('#jsTemplatePlaceholder_' + this.templateName).prevAll().each(function()
        {
            var matches;
            if ((matches = this.id.match(/\d+$/)) && matches[0] == iter) {
                $(this).remove();
                return false;
            }
            return true;
        });

        return this;
    },

    /**
     *
     */
    clear: function()
    {
        $('#jsTemplatePlaceholder_' + this.templateName).prevAll().remove();
        this.data = [];

        return this;
    },

    /**
     *
     */
    debugVars: function()
    {
        var vars = [];

        var isVar = true;
        $(this.html.split(/\[%% (\w+) %%\]/)).each(function()
        {
            if (isVar = !isVar) { vars.push(this); }
        });

        return vars;
    }
};

/*****************************************************************************
 * CAPTCHA
 *****************************************************************************/
/**
 * CaptchaImage
 *
 * Handles loading and displaying of captcha images.
 */
var CaptchaImage =
{
    key: null,

    isActive: false,

    setKey : function(key) { CaptchaImage.key = key; },

    /**
     * newCaptcha()
     * 
     * Creates a new captcha image in the provided div.
     *
     * @param string containerEl - id of div element into which captcha will be drawn
     * @param function callback - called after captcha image has loaded.
     */
    newCaptcha: function(containerEl, callback)
    {
        
        callback = (typeof(callback) == 'function') ? callback : function() {};

        if (CaptchaImage.isActive) {
            // We have to remove all elements of specific ids because we'll be creating
            // new ones.
            CaptchaImage.destroy();
        }

        CaptchaImage.isActive = true;

        // Create elements for Recaptcha to use.  We have to create these dynamically
        // to prevent multiple elements having the same id.
        $('<div id="captchaImageWrap">'
        + '<div id="recaptcha_image"></div>'
        + '</div>'
        + '<input id="recaptcha_response_field" type="text"'
        + ' name="captchaResponse"'
        + ' onfocus="CaptchaImage.hideResponseText();"'
        + ' onblur="CaptchaImage.showResponseText();" />'
        + '<div id="captcha_hint">'
        + "Can't read the words above?&#160;&#160;"
        + '<a href="javascript: void(0);" onclick="CaptchaImage.reload();">'
        + 'Try new words'
        + '</a>.&#160;&#160;'
        + 'Powered by <a href="http://recaptcha.net/" target="_new">ReCaptcha</a>.'
        + '</div>').appendTo('#' + containerEl);

        // Load captcha
        Recaptcha.create(CaptchaImage.key,
                          containerEl,
                         {'theme'    : 'custom',
                          'callback' : function() {
                               CaptchaImage.showResponseText();
                               callback(); }});
    },

    /**
     * hideResponseText()
     * 
     * Hides text inside the response field, called on focus of response field.
     */
    hideResponseText: function() {
        $('#recaptcha_response_field').val('');
        $('#recaptcha_response_field').removeClass('captchaResponseEmpty');
        //$('#recaptcha_response_field').css("background","url("+image_base+"post-content-span-input-focus.gif?version=0)");
    },

    /**
     * showResponseText()
     * 
     * Shows text inside the response field, called on blur of response field.
     */
    showResponseText: function() {
        if ($('#recaptcha_response_field').val() == '') {
            $('#recaptcha_response_field').addClass('captchaResponseEmpty');
            $('#recaptcha_response_field').val(
                    ' type the words you see above, separated by a space');
        }
        //$('#recaptcha_response_field').css("background","url("+image_base+"post-content-span-input.gif?version=0)");

    },

    /**
     * reload()
     * 
     * Reloads captcha image and resets response field text.
     */
    reload: function() {
        // Recaptcha.reload() will wipe out the text in the response field
        // we have to set it after the captcha has loaded.
        function blurIfReloaded()
        {
            if ($('#recaptcha_image img').length) {
                $('#recaptcha_response_field').blur();
            } else {
                setTimeout(blurIfReloaded, 50)
            }
        }

        $('#recaptcha_image').empty();
        Recaptcha.reload();
        blurIfReloaded();
    },

    /**
     * destroy()
     * 
     * Cancels Recaptcha and destroys all DOM elements created by newCaptcha().
     */
    destroy: function() {
        Recaptcha.destroy();
        
        $('#captchaImageWrap').remove();
        $('#recaptcha_response_field').remove();
        $('#captcha_hint').remove();

        CaptchaImage.isActive = false;
    }
}

/*****************************************************************************
 * EDITABLE SELECTS
 *****************************************************************************/
 var EditableSelect = {
    mousingOver : false,

    valuesLists : {},

    initValuesList: function(domId)
    {
        if (typeof(EditableSelect.valuesLists[domId]) != 'undefined') {
            return;
        }

        var list = {};
        $('#' + domId + '_editableSelectUl li').removeClass('selected')
                                                              .each(function() {
            list[$(this).html().trim()] = this.id;
        });

        EditableSelect.valuesLists[domId] = list;
    },

    selectIfValueMatches: function(domId)
    {
        var input = $('#' + domId);

        if (typeof(EditableSelect.valuesLists[domId][input.val()]) != 'undefined') {
            $('#' + EditableSelect.valuesLists[domId][input.val()]).trigger('click');
        }
    },

    handleInputKeypress: function(domId, e)
    {
        var keyCode = (typeof(e.which != 'undefined')) ? e.keyCode : e.which;

        var input = $('#' + domId);
        var selected = $('#' + domId + '_'
                     + input.attr('_selectedLi') + '_editableSelectLi');

        input.attr('_selectedLi', '_empty');
        selected = (selected.length != 0) ? selected : false;
        switch (keyCode) {
            case 0x26: // up
                if (selected) {
                    var prev = selected.prev();
                    if (prev.length > 0) { 
                        prev.trigger('click');
                        selected.removeClass('selected');
                    }
                } else {
                    $('#' + domId + '_editableSelectUl li:last').trigger('click');
                }
                break;
            case 0x28: // down
                if (selected) {
                    var next = selected.next();
                    if (next.length > 0) {
                        next.trigger('click'); 
                        selected.removeClass('selected');
                    }
                } else {
                    $('#' + domId + '_editableSelectUl li:first').trigger('click');
                }
                break;
            case 0x09: // tab
                EditableSelect.mousingOver = false;
                break;
            case 0x25: // left
            case 0x27: // right
            case 0x1B: // esc
                break;
            case 0x0D: // return
                EditableSelect.mousingOver = false;
                input.trigger('blur');
                break;
            default:
                if (selected) { selected.removeClass('selected'); }
                input.one('keyup', function() { EditableSelect.selectIfValueMatches(domId); });
        }
    },

    handleInputFocus: function(domId)
    {
        var input = $('#' + domId);
        var ul    = $('#' + domId + '_editableSelectUl');

        EditableSelect.initValuesList(domId);
        EditableSelect.selectIfValueMatches(domId);

        ul.show();
    },

    handleInputBlur: function(domId)
    {
        if (!EditableSelect.mousingOver) {
            $('#' + domId + '_editableSelectUl').hide();
        }
    },

    handleLiClick: function(domId, value)
    {
        var input = $('#' + domId);
        var li    = $('#' + domId + '_' + value + '_editableSelectLi');

        $('#' + domId + '_editableSelectUl li').removeClass('selected');
        li.addClass('selected');

        input.val(li.html().trim())
             .attr('_selectedLi', value)
             .trigger('focus');
    },

    handleLiMouseover: function(domId, value)
    {
        EditableSelect.mousingOver = true;
        $('#' + domId + '_' + value + '_editableSelectLi').addClass('highlighted');
    },

    handleLiMouseout: function(domId, value)
    {
        EditableSelect.mousingOver = false;
        $('#' + domId + '_' + value + '_editableSelectLi').removeClass('highlighted');
    }
 };
/*****************************************************************************
 * AJAX
 *****************************************************************************/
/**
 * Global Ajax Setup
 */
var AjaxDefaults = {
    dataType: 'json',

    /**
     * handleError()
     *
     * Global ajax error handling function.
     *
     * @param XmlHttpRequest req
     * @param string         errorType - 'error', 'parseerror' or 'timeout'
     */
    error: function(req, errorType)
    {
        this.onError(errorType, '', -1, req.responseText);
    },

    /**
     * handleSuccess()
     *
     * @param object data - response data json parsed by jquery
     */
    success: function(data)
    {
        // our JSON should have status field at the top level
        if (typeof(data.status) == 'undefined') {
            
            this.onError('badformat', '', -1, data);
        }

        var range = data.status & CommonConfig.AJAX_STATUS_RANGE_MASK;
        var code  = data.status & CommonConfig.AJAX_STATUS_CODE_MASK;

        switch (range) {
            case CommonConfig.AJAX_STATUS_SUCCESS_RANGE:
                this.onSuccess(data.response, code);
                break;
            case CommonConfig.AJAX_STATUS_USER_ERROR_RANGE:
                this.onError('usererror', data.message, code, data);
                break;
            default:
                this.onError('softwareerror', data.message, code, data);
        }
    },

    /**
     * onError - default error handler, can be overridden
     */
    onError  : function(type, message, code, data)
    {
    /*
if (code == -1) { alert(type + ': ' + data); }
else if (type != 'usererror') { alert(message); }
*/

        message = (type == 'usererror')
                ? message
                : 'There was an error communicating with the server';

        AlertMessage.triggerAlert('error', message);
    }, 

    onSuccess: function(){}
};

$.ajaxSetup(AjaxDefaults);

var AjaxValidators = {}; // will be filled by php

/**
 * Form utility class
 */
var Forms =
{
    /**
     *
     */
    getParamsFromDom: function(url, domPrefix)
    {
        url = url.trim('/');
        var params = {};
        var el;

        for (var i in AjaxValidators[url]) {
            if (typeof(AjaxValidators[url][i].radioOptions) != 'undefined') {
                // radios
                $(AjaxValidators[url][i].radioOptions).each(function() {
                    if (   ((el = $('#' + domPrefix + '_' + i + '_' + this)).length > 0)
                        && (el.attr('checked'))) {
                            params[i] = this;
                            return false;
                    }
                    return true;
                });
            } else {
                // not radios
                if ((el = $('#' + domPrefix + '_' + i)).length > 0) {
                    if (el.attr('type') == 'checkbox') {
                        params[i] = (el.attr('checked') ? 1 : 0);
                    } else {
                        params[i] = $('#' + domPrefix + '_' + i).val();
                    }
                }
            }
        }

        return params;
    },

    /**
     *
     */
    getValidationErrors: function(url, params)
    {
        url = url.trim('/');
        var errors = [];

        for (var i in AjaxValidators[url]) {
            var error = AjaxValidators[url][i](params[i]);
            if (error) { errors.push(error); }
        }

        return errors;
    },

    /**
     *
     */
    getParamsAndValidate: function(url, domPrefix, errorCallback)
    {
        url = url.trim('/');
        errorCallback = (typeof(errorCallback) == 'function')
                      ? errorCallback
                      : Forms.defaultValidationErrorHandler;

        var params = Forms.getParamsFromDom(url, domPrefix);
        var errors = Forms.getValidationErrors(url, params);

        if (errors.length > 0) {
            errorCallback(errors, params);
            return false;
        } else {
            return params;
        }
    },

    /**
     *
     */
    defaultValidationErrorHandler: function(errors, params)
    {
        AlertMessage.triggerAlert('error', 'Error: ' + errors[0]);
    }
}

/*****************************************************************************
 * APPLICATION
 *****************************************************************************/
var CommonConfig = {};
var Config       = {};
var ServerData   = {};

var Wurbble =
{

    /**
     *
     */
    handleSuitorMouseOver: function(lisuitor){
        $(lisuitor).addClass("suitorPhotoMouseOver");
        $(lisuitor).parent().find(".place").hide();
        $(lisuitor).parent().find(".place_mouseover").show();
    },
    handleSuitorMouseOut: function (lisuitor){
        $(lisuitor).removeClass("suitorPhotoMouseOver");
        $(lisuitor).parent().find(".place").show();
      	$(lisuitor).parent().find(".place_mouseover").hide();
    },
    handleSuitorNameMouseOver: function(lisuitor){
      	$(lisuitor).find(".name_name").hide();
      	$(lisuitor).find(".name_profile").show();
    },
    handleSuitorNameMouseOut: function (lisuitor){
        $(lisuitor).find(".name_profile").hide();
        $(lisuitor).find(".name_name").show();
    },


    /**
     *
     */
    updateVoteTotals: function(domId)
    {
        var matches = domId.match(/^(.*)_suitorVoteBar(\d+)_(\d+)$/);

        if (matches) {
            var ords = {'1' : '1st',
                        '2' : '2nd',
                        '3' : '3rd',
                        '4' : '4th'};

            var voted  = $('#' + domId);
            var parent = voted.parents('ul.votes:eq(0)');
            var votedCount = (Number)(voted.attr('wurbbleVoteCount'));
            voted.attr('wurbbleVoteCount', votedCount + 1);

            var notVoted = parent.find('span[id^=' + matches[1]
                                                   + '_suitorVoteBar'
                                                   + matches[2] + ']')
                                 .not(voted);

            var oldRank = (Number)(voted.html().substr(0, 1));

            var behind = [];
            var totalTied = 0;

            notVoted.each(function() {
                var notVotedCount = (Number)($(this).attr('wurbbleVoteCount'));

                if (notVotedCount == (votedCount + 1)) {
                    var rank = $(this).html().substr(0, 1);
                    voted.removeClass('vote' + ords[oldRank])
                         .addClass('vote' + ords[rank])
                         .html(ords[rank] + ' (tie)');

                    $(this).html(ords[rank] + ' (tie)');
                    totalTied++;
                } else if (notVotedCount == votedCount) {
                    behind.push(this);
                }
            });

            if (behind.length) {
                if (totalTied == 0) {
                    voted.html(ords[oldRank]);
                }

                $(behind).removeClass('vote' + ords[oldRank])
                         .addClass('vote' + ords[oldRank + 1])
                         .html(ords[oldRank + 1] + ((behind.length > 1) ? ' (tie)' : ''));

            }
        }
    },

    /**
     *
     */
    castVote: function(type, referenceId, id, inputDomIdOrCallback)
    {
        $.ajax({
            'url'      : '/voteservice/post/',
            'data'     : { 'selection' : id,
                           'voteOnId'  : referenceId,
                           'type'      : type },
            'type'     : 'GET',
            'dataType' : 'json',
            'onSuccess'  : function(data)
            {
                switch (typeof(inputDomIdOrCallback)) {
                    case 'string':
                        if ((type == 'SHOW_WURBBLE') || (type == 'WURBBLE')) {
                            Wurbble.updateVoteTotals(inputDomIdOrCallback);
                        }
                        //$('#' + inputDomIdOrCallback).htmlAddition(1, 'value');
                        break;
                    case 'function':
                        inputDomIdOrCallback();
                        break;
                    default:
                }
                AlertMessage.triggerAlert('success', 'Vote Submitted!');
            }
        });
    },

    /**
     *
     */
    showLightbox: function(lightboxName, data, callback)
    {
        callback = (typeof(callback) != 'undefined')
                 ? callback
                 : function() { return null; };

        var template = JsTemplate('lightbox_' + lightboxName);
        template.clear();
        template.render(data, null, '#lightbox_' + lightboxName + '_content');

        $('#lightbox_' + lightboxName).jqmShow(callback());
    },

    /**
     *
     */
    showWurbbleLightbox: function(wurbbleData, callback)
    {
        function ourCallback() { // render callback

            if (typeof(callback) == 'function') { callback(); }

            // will empty the lightbox once it's closed so that video stops playing
            return function() { // show callback
                return function() { // hide callback
                    $('#lightbox_viewWurbble_content').empty();
                };
            };
        }

        Wurbble.showLightbox('viewWurbble', wurbbleData, ourCallback);
        $('#viewWurbbleLightbox_wurbbleVideoJs').loadVideo(wurbbleData.videoFilename);

        if (wurbbleData.buttonsDisabled == 1) {
            $('#viewWurbbleLightbox_firstDotJs  a, '
            + '#viewWurbbleLightbox_secondDotJs a, '
            + '#viewWurbbleLightbox_thirdDotJs  a').removeAttr('href')
                                                   .removeAttr('onclick')
                                                   .addClass('disabled');
        }
    },

    /**
     *
     */
    viewWardrobe: function(wurbbleId, gender)
    {
        $.ajax({
            'url' : '/voteservice/getWardrobe/',
            'type' : 'GET',
            'data' : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function(data, status)
            {
                if (!data.outfits.length) {
                    AlertMessage.triggerAlert(
                        'success',
                        'There are no wardrobe options for this Wurbble.'
                    );
                    return;
                }
                if (gender == 'her') {
                    data.nominative = 'she';
                    data.genitive   = 'her';
                } else {
                    data.nominative = 'he';
                    data.genitive   = 'his';
                }
                Wurbble.showLightbox('wardrobe', data);
            }
        });
    },

    /**
     *
     */
    viewEject: function(wurbbleId, userName, gender)
    {
        $.ajax({
            'url'  : '/voteservice/ejectTotals/',
            'type' : 'GET',
            'data' : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function(data, status)
            {
                Wurbble.showLightbox(
                    'eject',
                    {
                        'ejectUserName' : userName,
                        'accusative'    : gender,
                        'wurbbleId'     : wurbbleId,
                        'percentage'    : data['percentage']
                    }
                );

            }
        });
    },

    /**
     *
     */
    castEjectVote: function(wurbbleId, selection)
    {
        Wurbble.castVote('EJECT', wurbbleId, selection, function()
        {
            $('#lightbox_eject').jqmHide();
        });
    },

    /****** VIDEO UPLOAD ******/

    onUploadCallback: function() {},

    /**
     *
     */
    showUploadLightbox: function(type, url, onHideCallback, onShowCallback)
    {
        Wurbble.onUploadCallback = (typeof(onHideCallback) == 'function')
                                 ? onHideCallback
                                 : function() {log('Video uploaded');};

        onShowCallback = (typeof(onShowCallback) == 'function')
                         ? onShowCallback
                         : function() {};

        Wurbble.showLightbox(
            'videoUpload',
            {
                'url' :  (typeof(url)  == 'string') ? url  : '/uploadservice/video/',
                'type' : (typeof(type) == 'string') ? type : 'user'
            },
            onShowCallback
        );
    },

    /**
     *
     */
    handleUploadSuccess: function(response, type)
    {
    /*
        if (type == 'comment') {
            Wurbble.Comments.handleUploadSuccess(response);
            return;
        }
        */

        var status = response.status & CommonConfig.AJAX_STATUS_CODE_MASK;

        switch (status) {
            case CommonConfig.VIDEO_UPLOAD_MUST_CONVERT:
                var text = 'Please wait while we convert your video.';
                break;
            case CommonConfig.VIDEO_UPLOAD_DONE_CONVERSION:
                var text = 'Video converted successfully';
                break;
            default:
                var text = 'Video uploaded successfully.';
        }

        AlertMessage.triggerAlert('success', text);

        var argCallback  = Wurbble.onUploadCallback(status, response.response);
        if (argCallback === false) { return; }
        if (typeof(argCallback) != 'function') { argCallback = function(){}; }

        var pageCallback = CurrentPage.onVideoUpload(status, response.response);
        if (pageCallback === false) { return; }
        if (typeof(pageCallback) != 'function') { pageCallback = function(){}; }

        $('#lightbox_videoUpload').jqmHide(function()
        {
            argCallback(status, response.response);
            pageCallback(status, response.response);
        });
    },

    /**
     *
     */
    handleUploadFailiure: function(response, type)
    {

        if (typeof(response.status) != 'undefined'
            && (response.status & CommonConfig.AJAX_STATUS_USER_ERROR_RANGE)) {
            
            AlertMessage.triggerAlert('error', response.message);
        } else {
            AlertMessage.triggerAlert('error', 'There was an error uploading your video');
        }
    //alert('ERROR: ' + response.message);
    },

    /**
     *
     */
    oldVideosCallback : null,

    showOldVideos: function(callback)
    {
        Wurbble.oldVideosCallback = callback;
        Wurbble.oldVideoGoPage(1);
    },

    /**
     *
     */
    oldVideoGoPage: function(pageNum)
    {
        $.ajax({
            'url' : '/accountservice/getVideos/',
            'type' : 'GET',
            'dataType' : 'json',
            'data' : { 'page' : pageNum },
            'onSuccess' : function(data, status)
            {
                Wurbble.showLightbox('useOldVideo', data);
            }
        });
    },

    /**
     *
     */
    useOldVideo: function(iter)
    {
        var videoIter = iter.split('_').pop();
        var videoData = JsTemplate('lightbox_useOldVideo')
                                        .getData('first').oldVideos[videoIter];

        $('#lightbox_useOldVideo').jqmHide(function()
        {
            Wurbble.oldVideosCallback(videoData);
        });
    },

    /****** GLOBAL FORM HANDLING ******/
    /**
     *
     */
    validateParamsAndShowAlertMessage: function(url, params)
    {
        // validate them
        if ((errors = Forms.getValidationErrors(url, params)).length > 0) {
            AlertMessage.triggerAlert('error', errors[0]);
            return false;
        }
        return true;
    },

    /**
     *
     */
    handleFormSubmit: function(url, domPrefix)
    {
        try {
            // get params
            var params = Forms.getParamsFromDom(url, domPrefix);

            // call specific handler
            var func = CurrentPage['on' + domPrefix.ucFirst() + 'FormSubmit'];
            if (typeof(func) == 'function') {
                response = func.call(CurrentPage, url, params);
                if (response === false)        { return false; }
                if (response instanceof Array) { params = response; }
            }

            var isSubmit = Wurbble.validateParamsAndShowAlertMessage(url, params);

            if (isSubmit) { $.clearAllHints(); }

            return isSubmit;

        } catch (e) { log(e); return false; }
    },

    /****** LOGIN/SIGNUP ******/
    /**
     *
     */
    handleLogin: function()
    {
        try {
            var url = 'loginservice/login';
            var params = Forms.getParamsAndValidate(
                'loginservice/login', 'header_signupForm'
            );

            if (params) {
                $.ajax(
                {
                    url     : '/loginservice/login/',
                    data    : params,
                    type    : 'POST',
                    onSuccess: function(response) {
                        Wurbble.userIsLoggedIn  = 1;
                        Wurbble.userIsConfirmed = parseInt(response.isConfirmed);

                        $('#header_signupForm').hide();
                        $('#header_loggedIn').show();
                        $('#header_loggedIn_username').text(response.username);
                        $('#header_loggedIn_profileLink').attr('href',
                                            '/users/' + response.username + '/');
                        $('#header_loggedIn_messageCount').text(response.messageCount);
                        $('#header_loggedIn_avatar').attr('src',
                                                          response.thumbSrc);
                        if (response.isAdmin == 1) { $('#navigation_admin').show(); }

                        CurrentPage.onLogin();
                    }
                }); // ajax
            }
        } catch(e) { log(e); }

        return false;
    },

    /**
     *
     */
    handleSignup: function()
    {
        var email = $('#header_signupForm_email').val().trim();
        if (!Wurbble.toSignupPage(email)) { return false; }
        $.clearAllHints();
        $('#header_signupForm').trigger('submit');
        return false;
    },

    /**
     *
     */
    handleLoginOrSignup: function()
    {
        try{
            if ($('#header_signupForm_password').val() != '') {
                return Wurbble.handleLogin();
            } else {
                return Wurbble.handleSignup();
            }
        } catch(e) { log(e); return false; }
    },

    /**
     *
     */
    handleLogout: function()
    {
        if (CurrentPage.isLoginPage) {
            if (!confirm('If you log out you will be redirected to the home page')) {
                return false;
            }
        }

        // call page specific logout handler
        if (!CurrentPage.onLogout()) { return false; }

        // log out
        $.ajax(
        {
            url: '/loginservice/logout/',
            onSuccess: function()
            {
                if (CurrentPage.onLogoutComplete()) {
                    if (CurrentPage.isLoginPage) {
                        document.location.href = '/';
                    } else {
                        Wurbble.userIsLoggedIn = 0;
                        $('#header_signupForm').show();
                        $('#header_loggedIn').hide();
                        $('#navigation_admin').hide();
                    }
                }
            }
        }); // ajax
    },

    /**
     *
     */
    validateBirthDay: function(idBase)
    {
        var now = new Date();

        var components = {};

        $(['year', 'month', 'day']).each(function() {
            components[this] = $('#' + idBase + '_birthDate' + this.ucFirst()).val();
        });

        if ((now.getFullYear() - 18) > components['year'])  { return true; }
        if ((now.getMonth() + 1) > components['month']) { return true; }
        if (now.getDate()       >= components['day'])   { return true; }

        $('#' + idBase + '_birthDateMonth').selectByValue(
            ((String)(now.getMonth() + 1)).strpadLeft(1, '0')
        );
        $('#' + idBase + '_birthDateDay').selectByValue(now.getDate());

        AlertMessage.triggerAlert(
            'error',
            'You must be at least 18 years of age to participate'
        );

        return false;
    },

    /**
     *
     */
    resetPassword: function()
    {
        Wurbble.showLightbox('forgotPassword', {}, function()
        {
            CaptchaImage.newCaptcha('resetPassword_captcha');
        });
    },

    /**
     *
     */
    validateLoggedInUser: function(rejectHidden, rejectUnconfirmed)
    {
        if (!Wurbble.userIsLoggedIn) {

            AlertMessage.triggerAlert('error', 'Please log in first.');
            return false;

        } else if (   Wurbble.userProfileIsHidden
                   && typeof(rejectHidden) != 'undefined'
                   && rejectHidden
        ) {

            Wurbble.showLightbox('privateProfile', {});
            return false;

        } else if (   !Wurbble.userIsConfirmed
                   && typeof(rejectUnconfirmed) != 'undefined'
                   && rejectUnconfirmed) {

            AlertMessage.triggerAlert('error', 'Please confirm your email first.');
            return false;
        }

        return true;
    },

    /****** MESSAGES ******/
    /**
     *
     */
    composeMessage: function(userId, userName, type)
    {
        if (!Wurbble.validateLoggedInUser()) { return; }
        type = (typeof(type) == 'undefined') ? 'standard' : type;

        Wurbble.showLightbox(
            'sendMessage',
            {
                'msgType'  : type,
                'userName' : userName,
                'userId'   : userId
            }
        );
    },

    /**
     *
     */
    uploadVideoMessage: function()
    {
        $('#lightbox_sendMessage').jqmHide(function()
        {
            Wurbble.showUploadLightbox(
                'message',
                null,
                function(status, response) {
                    if (status & CommonConfig.VIDEO_UPLOAD_DONE_CONVERSION) {
                        CurrentPage.messageHasVideo = 1;
                        $('#sendMessage_video').loadVideo(response.filename)
                                               .show();
                        return function() { $('#lightbox_sendMessage').jqmShow(); };
                    } else {
                        return false;
                    }
                }
            );
        });
    },

    /**
     *
     */
    recordVideoMessage: function()
    {
        $('#lightbox_sendMessage').jqmHide(function()
        {
            Wurbble.Recorder.show(function(filename)
            {
                CurrentPage.messageHasVideo = 1;
                $('#sendMessage_video').loadVideo(filename);
                $('#lightbox_sendMessage').jqmShow();
            });
        });
    },

    /**
     *
     */
    useOldVideoForMessage: function()
    {
        $('#lightbox_sendMessage').jqmHide(function()
        {
            Wurbble.showOldVideos(function(videoData)
            {
                $('#sendMessage_video').loadVideo(videoData.filename);
                $('#sendMessage_useOldVideo').val(videoData.id);
                $('#lightbox_sendMessage').jqmShow();
            });
        });
    },

    /**
     *
     */
    sendMessageFromWurbble: function(userId, userName)
    {
        if (!Wurbble.validateLoggedInUser()) { return; }

        if ($('#lightbox_viewWurbble').length && $('#lightbox_viewWurbble').is(':visible')) {
            $('#lightbox_viewWurbble').jqmHide(function()
            {
                Wurbble.composeMessage(userId, userName);
            });
        } else {
            Wurbble.composeMessage(userId, userName);
        }
    },

    /****** ASSORTED ******/
    /**
     *
     */
    handleTabClick: function(tabId)
    {
        // same page
        if (!CurrentPage.onTabClick(tabId)) { return false; }

        // account page
        if ((tabId == 'account') || (tabId == 'create')) { return Wurbble.toAccountPage(); }

        return true;
    },

    /**
     *
     */
    handleSubTabClick: function(selection, el)
    {
        $el = $(el);
        $el.parents('ul:eq(0)').children('li').removeClass('selected');
        $el.parents('li:eq(0)').addClass('selected');
        CurrentPage.goTab(selection);
    },

    /**
     *
     */
    addFavorite: function(favoriteId)
    {
        if (!Wurbble.validateLoggedInUser()) { return; }

        $.ajax({
            'url'      : '/favoriteservice/add/',
            'type'     : 'POST',
            'data'     : { 'userId' : favoriteId },
            onSuccess  : function(status, response)
            {
                AlertMessage.triggerAlert('success', 'Favorite Added!');
            }
        });
    },

    /**
     *
     */
    sendInvite: function(userId, callback, userName)
    {
        if (!Wurbble.validateLoggedInUser(true)) { return; }

        callback = (typeof(callback) == 'function') ? callback : function() {};
        $.ajax({
            url: '/wurbbleservice/inviteUser/',
            data: { 'userId' : userId },
            type: 'POST',
            onSuccess: function(response)
            {
                AlertMessage.triggerAlert('success', userName + ' invited!');
                callback(response);
            }
        });
    },

    /**
     *
     */
    publishWurbble: function(idBase, iter, wurbbleId)
    {
        $.ajax({
            'url'       : '/wurbbleservice/publish/',
            'type'      : 'POST',
            'data'      : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function(data, status)
            {
                AlertMessage.triggerAlert('success', 'Wurbble published!');
                $('#' + idBase + '_publishLink' + iter).remove();
                $('#' + idBase + '_pubDate' + iter).html('CLOSES AFTER 1 WEEK');
            }
        });
    },

    /**
     *
     */
    closeWurbble: function(idBase, iter, wurbbleId)
    {
        if (!confirm('Are you sure you want to close this Wurbble?')) { return; }

        $.ajax({
            'url'       : '/wurbbleservice/close/',
            'type'      : 'POST',
            'data'      : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function(data, status)
            {
                AlertMessage.triggerAlert('success', 'Wurbble closed!');
                $('#' + idBase + '_closeLink' + iter).remove();
                $('#' + idBase + '_pubDate' + iter).html('CLOSED');
            }
        });
    },

    /**
     *
     */
    deleteWurbble: function(idBase, iter, wurbbleId)
    {
        if (!confirm('Are you sure you want to delete this Wurbble?')) { return; }

        $.ajax({
            'url'       : '/wurbbleservice/delete/',
            'type'      : 'POST',
            'dataType'  : 'json',
            'data'      : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function(data, status)
            {
                AlertMessage.triggerAlert('success', 'Wurbble deleted!');
                $('#' + idBase + '_wurbbleOnPageWrap' + iter).remove();
            }
        });
    },

    /**
     *
     */
    onPageNav: function(section, pageNum)
    {
        if (pageNum < 1) { return false; }

        if (section == 'oldVideo') {
            Wurbble.oldVideoGoPage(pageNum);
            return false;
        }

        var matches = section.match(/(\w+)_comments/);
        if (matches) {
            Wurbble.Comments.view(matches[1], null, null, pageNum);
            return false;
        }

        return CurrentPage.onPageNav(section, pageNum);
    },

    /**
     *
     */
    addFeaturedWurbble: function(wurbbleId)
    {
        $.ajax({
            'url'  : '/adminservice/addFeaturedWurbble/',
            'type' : 'POST',
            'data' : { 'wurbbleId' : wurbbleId },
            'onSuccess' : function()
            {
                AlertMessage.triggerAlert('success', 'Wurbble added!');
            }
        });
    },

    /**
     *
     */
    toAccountPage: function()
    {
        if (!CommonConfig.IS_BETA || Wurbble.userIsLoggedIn) { return true; }

        AlertMessage.triggerAlert('error', 'Please login or signup first using the form at the top right hand corner of the page.');
        return false;
    },

    /**
     *
     */
    toSignupPage: function(emailPassthru)
    {
        if (!CommonConfig.IS_BETA || Wurbble.userIsLoggedIn) { return true; }

        Beta.showForm(emailPassthru);
        return false;
    },

    /**
     *
     */
    showPrivateProfileWarning: function()
    {
        if (Wurbble.userProfileIsHidden) {
            $('<div id="privateWarning">'
            + '  <a href="javascript:void(0);" onclick="Wurbble.makeAccountPublic();">' 
            + '     Make this profile ACTIVE'
            + '  </a>'
            + '</div>').css(
                {
                  'position'    : 'absolute',
                  'top'         : '10px',
                  'left'        : '10px',
                  'width'       : '400px',
                  'padding'     : '25px',
                  'font-size'   : '25px',
                  'font-weight' : 'bold',
                  'text-align'  : 'center',
                  'background'  : '#BBAAFF',
                  'border'      : '2px solid #441177'
                }
            ).appendTo('body');
        }
    },

    /**
     *
     */
    makeAccountPublic: function()
    {
        $.ajax({
            'url'  : '/accountservice/makePublic/',
            'type' : 'POST',
            'onSuccess' : function()
            {
                $('#account_info_privacy0').attr('checked', 1);

                $('#privateWarning').html(
                    'Thank you, your profile is now active! '
                  + '<a href="/create/">Click here to start creating a Wurbble!<a>'
                ).css(
                    {
                      'font-size' : '18px'
                    }
                );
            }
        });
    },

    /**
     * Comments
     */
    Comments:
    {
        commentsOpened: {},

        /**
         *
         */
        view: function(section, type, id, page)
        {
            if (type === null) {
                var wasOpen = true;
                type    = Wurbble.Comments.commentsOpened[section].type;
                id      = Wurbble.Comments.commentsOpened[section].id;
            } else {
                var wasOpen = false;
                Wurbble.Comments.commentsOpened[section] = {'type'     : type,
                                                            'id'       : id };
            }

            $.ajax({
                'type'     : 'GET',
                'url'      : '/commentservice/get/',
                'data'     : { 'type' : type,
                               'id'   : id,
                               'page' : page },
                'onSuccess' : function(response)
                {

                    function drawComments()
                    {
                        Wurbble.Comments.movePostFormToTop(section, false);

                        // clear out all the old comment posting stuff
                        Wurbble.Comments.hasVideo = 0;
                        Wurbble.Comments.clearPostForm(section + '_postCommentBottom');

                        // render comments
                        $('#' + section + '_viewVideo').hide();
                        var templater = JsTemplate(section + '_comment');
                        templater.clear();
                        templater.render(response.commentList);

                        // load videos if necessary
                        $(response.commentList).each(function(i) {
                            if (this.videoFileName && this.videoFileName.length > 0) {
                                $('#' + section + '_commentVideo' + i).loadVideo(this.videoFileName);
                            }
                        });

                        // render pager
                        var pageTemplater = JsTemplate(section + '_commentPager');
                        pageTemplater.clear();
                        pageTemplater.render(response.pageData);

                        // set total at the top
                        $('#' + section + '_totalComments').html(response.pageData.totalCount);

                        // show comments
                        if (wasOpen) {
                            $('#' + section + '_commentWrap').fadeIn('fast');
                        } else {
                            $('#' + section + '_commentWrap').slideDown();
                        }
                    }


                    if (wasOpen) {
                        $('#' + section + '_commentWrap').fadeOut('fast', drawComments);
                    } else {
                        drawComments();
                    }
                }
            });
        },

        /**
         *
         */
        hide: function(section)
        {
            $('#' + section + '_commentWrap').slideUp();
        },

        /**
         *
         */
        clearPostForm: function(formIdBase)
        {
            $('#' + formIdBase + '_form_replyToCommentId').val(0);
            $('#' + formIdBase + '_form_text').val('');
            $('#' + formIdBase + '_viewVideo').hide();
        },

        /**
         *
         */
        movePostFormToTop: function(idBase, show)
        {
            Wurbble.Comments.clearPostForm(idBase + '_postCommentMobile');

            $('#' + idBase + '_postCommentMobile_formWrap').insertAfter('#' + idBase + '_commentHeader');

            if ((typeof(show) != 'undefined') && (show !== null)) {
                if (show) {
                    $('#' + idBase + '_postCommentMobile_formWrap').show();
                } else {
                    $('#' + idBase + '_postCommentMobile_formWrap').hide();
                }
            }
        },

        /**
         *
         */
        showPostForm: function(idBase)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return; }
            Wurbble.Comments.movePostFormToTop(idBase, true);
        },

        /**
         *
         */
        reply: function(idBase, type, referenceid, replyToId, iter)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return false; }

            $('#' + idBase + '_postCommentMobile_formWrap').insertAfter('#' + idBase + '_comment' + iter)
                                               .show();

            $('#' + idBase + '_postCommentMobile_form_replyToCommentId').val(replyToId);
        },

        /**
         *
         */
        post: function(formIdBase, type, referenceId)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return false; }

            var url = '/commentservice/post/';
            var params = Forms.getParamsFromDom('/commentservice/post/', formIdBase + '_form')

            params.hasVideo = Wurbble.Comments.hasVideo;

            $.ajax({
                'url'       : url,
                'type'      : 'POST',
                'data'      : params,
                'onSuccess' : function(response, status)
                {
                    AlertMessage.triggerAlert('success', 'Comment posted!');

                    var parts = formIdBase.split('_');
                    var formSuffix = parts.pop();
                    var commentIdBase = parts.join('_');

                    if (formSuffix == 'postCommentMobile') {
                        $('#' + commentIdBase + '_postCommentMobile_formWrap').hide();
                    } else {
                        Wurbble.Comments.clearPostForm(commentIdBase + '_postCommentBottom');
                    }

                    var iter = JsTemplate(commentIdBase + '_comment').render(
                        response.commentData,
                        null, // no callback
                        false // prepend
                    );

                    if (Wurbble.Comments.hasVideo) {
                        $('#' + commentIdBase + '_commentVideo' + iter).loadVideo(
                            response.commentData.videoFileName
                        );

                        Wurbble.Comments.hasVideo = 0;
                    }

                }
            });
            return false;
        },

        /**
         *
         */
        sendingVideo:   false,
        requestTimeout: 0,

        hasVideo: 0,

        inLightbox: function(idBase)
        {
            // are we already in a popup?
            var matches = idBase.match(/^.*(?=_[^_]*$)/);
            if (matches) {
                switch(matches[0]) {
                    case 'viewWurbbleLightboxJs':
                        return 'viewWurbble';
                        break;
                    case 'eject':
                        return 'eject';
                        break;
                    default:
                }
            }

            return false;
        },

        recordVideo: function(idBase)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return; }

            var inLightbox = Wurbble.Comments.inLightbox(idBase);

            function showRecorder() {
                Wurbble.Recorder.show(function(filename)
                {
                    if (inLightbox) { $('#lightbox_' + inLightbox).jqmShow(); }

                    Wurbble.Comments.hasVideo = 1;
                    $('#' + idBase + '_viewVideo').loadVideo(filename).show();
                });
                return false; // so default callback doesn't get called
            }

            if (inLightbox) {
                $('#lightbox_' + inLightbox).jqmHide(showRecorder);
            } else {
                showRecorder();
            }
        },

        uploadVideo: function(idBase)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return false; }

            var inLightbox = Wurbble.Comments.inLightbox(idBase);

            function showUploadLightbox() {
                Wurbble.showUploadLightbox(
                    'comment',
                    null,
                    function(status, data)
                    {
                        if (status & CommonConfig.VIDEO_UPLOAD_READY_TO_DISPLAY) {
                            Wurbble.Comments.hasVideo = 1;
                            $('#' + idBase + '_viewVideo').loadVideo(data.filename).show();
                        }

                        return (inLightbox)
                               ? function() { $('#lightbox_' + inLightbox).jqmShow(); }
                               : true;
                    }
                );
                return false;
            }

            if (inLightbox) {
                $('#lightbox_' + inLightbox).jqmHide(showUploadLightbox);
            } else {
                showUploadLightbox();
            }
        },

        /**
         *
         */
        reportSpam: function(commentId)
        {
            $.ajax({
                'url'  : '/commentservice/spam/',
                'type' : 'POST',
                'data' : { 'commentId' : commentId },
                'dataType' : 'json',
                'onSuccess' : function()
                {
                    AlertMessage.triggerAlert('success', 'Report Sent.');
                }
            });
        }

    }, // END Comments

    Paging:
    {
    }, // END Paging

    /**
     *
     */
    DateHer:
    {
        show: function(userId, userName, gender)
        {
            if (!Wurbble.validateLoggedInUser(false, true)) { return; }

            Wurbble.DateHer.hasVideo = 0;
            Wurbble.showLightbox('dateHer',
                                 {'dateHerUserId' : userId,
                                  'dateUserName'  : userName,
                                  'accusative'    : gender.ucFirst()});
        },

        sendingVideo: false,

        hasVideo: 0,

        /**
         *
         */
        recordVideo: function()
        {
            $('#lightbox_dateHer').jqmHide(function()
            {
                Wurbble.Recorder.show(function(filename)
                {
                    Wurbble.DateHer.hasVideo = 1;
                    $('#dateHerVideo').loadVideo(filename);
                    $('#lightbox_dateHer').jqmShow();
                });
            });
        },

        /**
         *
         */
        uploadVideo: function()
        {
            $('#lightbox_dateHer').jqmHide(function()
            {
                Wurbble.showUploadLightbox('dateHer', null, function(status, data)
                {
                    if (status == CommonConfig.VIDEO_UPLOAD_DONE_CONVERSION) {
                        return function() {
                            Wurbble.DateHer.hasVideo = 1;
                            $('#dateHerVideo').loadVideo(data.filename);
                            $('#lightbox_dateHer').jqmShow();
                        };
                    }
                    return false;
                });
            });
        },

/*
        handleUploadClick: function()
        {
            if (Wurbble.DateHer.sendingVideo) { return; }

            var intervalPointer = setTimeout(testUploadFormContents, 2000);

            function testUploadFormContents()
            {
                var filename = $('#dateHerVideoUpload_video').val();
                if (filename.length > 0) {
                    Wurbble.DateHer.sendingVideo = true;
                    $('#dateHerVideoUpload_submit').trigger('click');
                    $('#dateHerVideo').html('Uploading: ' + filename)
                                                  .show();
                    Wurbble.DateHer.hasVideo = 1;
                } else {
                    intervalPointer = setTimeout(testUploadFormContents, 2000);
                }
            }

            $('body').one('click', function() {
                $('body').one('click', function() {
                    clearTimeout(intervalPointer);
                });
            });
        },

        handleUploadSuccess: function(response)
        {
            var status = response.status & CommonConfig.AJAX_STATUS_CODE_MASK;

            if (status == CommonConfig.VIDEO_UPLOAD_MUST_CONVERT) {
                $('#dateHerVideo').html('Converting...');
            } else {
                $('#dateHerVideo').loadVideo(response.response.filename);
                // clear the file input so that we don't try and upload the
                // same video twice
                var el = $('#dateHerVideoUpload_video');
                var nextEl = el.next();

                var newEl = $(
                    '<input type="file" '
                         + 'id="dateHerVideoUpload_video" '
                         + 'name="' + el.attr('name') + '" />'
                );

                el.remove();
                newEl.insertBefore(nextEl);

                Wurbble.DateHer.sendingVideo = false;
            }
        },

        handleUploadFailiure: function(response)
        {
            Wurbble.DateHer.sendingVideo = false;
        },
        */

        /**
         *
         */
        send: function()
        {
            if (!Wurbble.DateHer.hasVideo) {
                AlertMessage.triggerAlert('error', 'You must upload a video first');
                $('#lightbox_dateHer').jqmHide();
                return false;
            }

            if (!$('#dateRequest_agreeToTerms').attr('checked')) {
                AlertMessage.triggerAlert('error', 'You must agree to the terms');
                return false;
            }

            var url = '/dateservice/postRequest/';
            var params = Forms.getParamsFromDom(url, 'dateRequest')

            $.ajax({
                'url'       : url,
                'type'      : 'POST',
                'dataType'  : 'json',
                'data'      : params,
                'onSuccess' : function(response, status)
                {
                    AlertMessage.triggerAlert('success', 'Request sent!');
                    Wurbble.DateHer.hasVideo = 0;
                    $('#lightbox_dateHer').jqmHide();
                }
            });

            return false;
        }
    },

    /**
     *
     */
    Recorder:
    {
        callback: null,
        noAjax:   false,

        show: function(callback, noAjax, onShowCallback)
        {
            Wurbble.Recorder.callback = callback;
            Wurbble.Recorder.noAjax   = (typeof(noAjax) != 'undefined' && noAjax) ? true : false;

            Wurbble.showLightbox('recordVideo', {});
            //var recorderSwf = new SWFObject('/public/flash/red5recorder.swf', 'flashRecorder','320','250','9');
            //var recorderSwf = new SWFObject('/public/flash/danRecorder.v0.swf', 'flashRecorder','320','275','9');
            var recorderSwf = new SWFObject(
                Config.flashUrls.recorder,
                'flashRecorder',
                '320',
                '275',
                '9'
            );

            recorderSwf.addParam("quality",           "medium");
            recorderSwf.addParam("allowfullscreen",   "true");
            recorderSwf.addParam("allowscriptaccess", "always");

            var sessionId = document.cookie.match(/PHPSESSID=(\w+)/)[1];

            recorderSwf.addVariable("maxLength", 10);
            recorderSwf.addVariable("fps", 8);
            recorderSwf.addVariable("recordingServer",    CommonConfig.VIDEO_RECORD_HOST);
            recorderSwf.addVariable("recordServer",    CommonConfig.VIDEO_RECORD_HOST);
           // recorderSwf.addVariable("playbackServer", 'http://playback.pam.terabolic.com/');
            recorderSwf.addVariable("playbackServer", CommonConfig.PLAYBACK_HOST);

            recorderSwf.addVariable("fileName",  sessionId);
            recorderSwf.addVariable("mode",      "record");

            recorderSwf.write('flashRecorderContainer');
	//$('#flashRecorderContainer').html('<embed id="flashRecorder" width="320" height="275"flashvars="recordingServer=rtmp://alpha1.wurbble.com:1935/oflaDemo/&playbackServer=rtmp://alpha1.wurbble.com:1935/oflaDemo/&fileName=' + sessionId + '&mode=record" allowscriptaccess="always" allowfullscreen="true" quality="medium" name="flashRecorder" style="" src="/public/flash/videoPlayer.v0.swf" type="application/x-shockwave-flash"/>');
            if (typeof(onShowCallback) == 'function') { onShowCallback(); }
        },

        done: function()
        {
            function close(filename)
            {
                $('#lightbox_recordVideo').jqmHide(function()
                {
                    switch (typeof(Wurbble.Recorder.callback)) {
                        case 'function':
                            Wurbble.Recorder.callback(filename);
                            break;
                        case 'string':
                            $(Wurbble.Recorder.callback).loadVideo(filename).show();
                            break;
                        default:
                    }
                });
            }

            if (Wurbble.Recorder.noAjax) {
                close(null);
            } else {
                $.ajax({
                    'url'  : '/uploadservice/notifyRecordComplete/',
                    'type' : 'POST',
                    'onSuccess' : function(data, status)
                    {
                        close(data.filename);
                    }
                });
            }
        }
    }
}; // END Wurbble

/**
 * Page - This is where we define handlers for events that could happen on any
 *        page but may be handled by the different pages.
 */
var CurrentPage = null;

var Page = function(extend) {
    if (extend == '_extend') { return; }
    this.init()
};

Page.prototype = 
{
    parent: Page.prototype,
    // will be overridden by the page
    isLoginPage     : false,
    messageHasVideo : 0,

    init            : function() { CurrentPage = this; },
    onLoad          : function() { return true; },
    onLogin         : function() { return true; },
    onLogout        : function() { return true; },
    onLogoutComplete: function() { return true; },
    onTabClick      : function() { return true; },
    onVideoUpload   : function() { return true; },
    goTab           : function() { return true; },
    onPageNav       : function() { return true; },

    // global handlers that may be overridden
    onSendMessageFormSubmit: function(url, params, callback)
    {
        if (!Wurbble.validateParamsAndShowAlertMessage(url, params)) { return false; }
        params.hasVideo = this.messageHasVideo;

        if (typeof(callback) != 'function') { callback = function(){}; }

        $.ajax({
            url:  url,
            data: params,
            type: 'POST',
            onSuccess: function()
            {
                if (callback() !== false) {
                    AlertMessage.triggerAlert('success', 'Message Sent');
                    $('#lightbox_sendMessage').jqmHide();
                }
            }
        });

        return false;
    },

    /**
     *
     */
    onResetPasswordFormSubmit: function(url, params)
    {
        params.captchaChallengeId = Recaptcha.get_challenge();
        params.captchaResponse    = Recaptcha.get_response();

        if (!Wurbble.validateParamsAndShowAlertMessage(url, params)) { return false; }

        $.ajax({
            'url'  : url,
            'type' : 'POST',
            'data' : params,
            'onSuccess' : function()
            {
                AlertMessage.triggerAlert(
                    'success',
                    'Instructions to reset your password have been sent to you email'
                );

                $('#lightbox_forgotPassword').jqmHide();
            },
            'onError' : function()
            {
                CaptchaImage.reload();
                AjaxDefaults.onError.apply(this, arguments);
            }
        });

        return false;
    }

};

// SearchPage extends Page
function SearchPage() { this.init(); } SearchPage.prototype = new Page('_extend');
$.extend(SearchPage.prototype,
{
    searchIdBase : '',

    /**
     *
     */
    handleAnyClick: function(field)
    {
        $('#' + this.searchIdBase + 'search_any_' + field)
                                     .nextAll('input').removeAttr('checked');
    },

    /**
     *
     */
    handleAdvancedOptClick: function(field)
    {
        $('#' + this.searchIdBase + 'search_any_' + field).removeAttr('checked');
    },

    /**
     *
     */
    toggleAdvanced: function()
    {
        var advSearchInput = $('#' + this.searchIdBase + 'search_isAdvancedSearch');
        var advSearchPane  = $('#' + this.searchIdBase + 'search_advanced');

        if (advSearchInput.val() != 1) {
            advSearchPane.slideDown('fast');
            advSearchInput.val(1);
        } else {
            advSearchPane.slideUp('fast');
            advSearchInput.val(0);
        }
    },

    /**
     *
     */
    validateGenderSelections: function()
    {
/*
        if (   !$('#' + this.searchIdBase + 'search_hostSearchMen').attr('checked')
            && !$('#' + this.searchIdBase + 'search_hostSearchWomen').attr('checked')) {

            AlertMessage.triggerAlert('error', 'Please select a gender for "Search For"');
            return false;
        }

        if (   !$('#' + this.searchIdBase + 'search_videosFromMen').attr('checked')
            && !$('#' + this.searchIdBase + 'search_videosFromWomen').attr('checked')) {

            AlertMessage.triggerAlert('error', 'Please select a gender for "Who Are Looking For"');
            return false;
        }
*/
        return true;
    }
});


var Beta = {
    emailPassthru : null,

    showForm: function(emailPassthru)
    {
        Beta.emailPassthru = (typeof(emailPassthru) == 'string')
                           ? emailPassthru
                           : null;

        Wurbble.showLightbox('beta', {});
    },

    validateCode: function() {
        $('#signup1_betaCode').val('beta code');
        $.ajax({
            'url'      : '/signupservice/validateBetaCode/',
            'data'     : {'betaCode' : $('#betaCode').val()},
            'type'     : 'GET',
            'dataType' : 'json',
            'onSuccess'  : function(data)
            {
                AlertMessage.triggerAlert('success', 'Submitted Code is valid');

                setTimeout(function() {
                    var params = { 'betaCode' : $('#betaCode').val() };
                    if (Beta.emailPassthru) { params.email = Beta.emailPassthru; }

                    Utils.navigateViaPost(
                        '/signup/', 
                        params
                    );
                    }, 500);
            },

            'onError'  : function(data)
            {
                $('#signup1_betaCode').val($('#betaCode').val());
                AlertMessage.triggerAlert('error', 'Submitted Code is invalid');
            }
        });
    },

    handleCodeRequest: function()
    {
        var email = $('#betaRequestEmail').val().trim();

        if (email == '') {
            AlertMessage.triggerAlert('error',
                'Please enter an email address so that we can send you a beta code');
            return false;
        }

        return true;
    }
};

// the global onload handler
$(function()
{
    if (!CurrentPage) { CurrentPage = new Page(); }

    // attach effects
    for (var i in Config.inputHints) { $('#' + i).setHint(Config.inputHints[i]); }

    for (var i in Config.lightboxes) {
        $('#' + i).jqm({
            'onShow': WurbbleJqm.handleShow,
            'onHide': WurbbleJqm.handleHide
        });
    }

    $('#header_signupForm_email')   .setHint('email/username');
    $('#header_signupForm_password').setHint('password');

    // look at server data
    if (typeof(ServerData.initialAlertMessage) == 'string') {
        var severity = (typeof(ServerData.initialAlertMessageSeverity) == 'string')
                     ? ServerData.initialAlertMessageSeverity : 'error';

        AlertMessage.triggerAlert(severity, ServerData.initialAlertMessage);

        var extra = (typeof(ServerData.initialAlertMessageExtra) == 'string')
                  ? ServerData.initialAlertMessageExtra
                  : '';
                    
        if (extra == 'betaRejected') {
            Beta.showForm();
        }
    }

    Wurbble.userIsLoggedIn      = ServerData.userIsLoggedIn;
    Wurbble.userProfileIsHidden = ServerData.userProfileIsHidden;
    Wurbble.userIsConfirmed     = ServerData.userIsConfirmed;

    CaptchaImage.setKey(CommonConfig.RECAPTCHA_PUBLIC_KEY);

    if (ServerData.rejectHiddenProfile) {
        Wurbble.showLightbox('privateProfile');
    }

    CurrentPage.onLoad(ServerData.actionName);
});


// Hide All Alerts When The Page Is Scrolled
$(window).scroll(function () { AlertMessage.hideAlertNow(); });
