/***
 **
 ** begin: ALERT FUNCTIONALITY
 **
 ***/

// Create new Veri.Alert Namespace
if (Veri.BrowserDetect == null || typeof(Veri.BrowserDetect) != "object") { Veri.BrowserDetect = new Object(); }

Veri.BrowserDetect = {

    check : function()
    {

        // If we've already warned the user this session
        if (document.cookie.match(/BROWSERWARN=1/))
            return; 

        var action = 'fail';
        if (jQuery.browser.safari && parseInt(jQuery.browser.version.substr(0,3)) >= 522) // Safari 3, 4
            action = 'pass';
        else if (jQuery.browser.mozilla && jQuery.browser.version.substr(0,3) == "1.9") // FF3
            action = 'pass';
        else if (jQuery.browser.msie && jQuery.browser.version.substr(0,1) == "7") // IE7
            action = 'pass';
        else if (jQuery.browser.msie && jQuery.browser.version.substr(0,1) == "8") // IE8
            action = 'pass';
        else if (navigator.userAgent.match('/chrome/i')) // Chrome
            action = 'pass';
        else if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i)) // iLove
            action = 'pass'; 
        else if (jQuery.browser.mozilla)
            action = 'warn';
        else if (jQuery.browser.opera)
            action = 'warn';

        if (action == 'pass'){
            return;
        }
        else if (action == 'warn'){
            alert("Oh man, we haven't really tested things in your browser. " + 
                  "The site may not work correctly. For an optimal experience " + 
                  "we recommend using: Firefox 3+, Safari 3+, IE7+, or Chrome." );
            // Set cookie so we don't see this message again for 1 month
            var oneMonth = new Date();
            oneMonth.setMonth( oneMonth.getMonth() + 1 );
            document.cookie = 'BROWSERWARN=1;domain=' + base_url.replace(/^http:\/\//, '') + ';expires='+oneMonth.toUTCString() + ";";
        }
        else {
            document.location = '/badbrowser/';
        }

    }

}

// ************** DO THE BROWSER CHECK **************
Veri.BrowserDetect.check();
// ************** DO THE BROWSER CHECK **************

// Create new Veri.Alert Namespace
if (Veri.Alert == null || typeof(Veri.Alert) != "object") { Veri.Alert = new Object(); }

// Begin Definition
Veri.Alert = {

    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(), 

    /*
     * 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(){ Veri.Alert.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(js_fade_speed,function(){
            Veri.Alert.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){
            Veri.Alert.alertsQueue.shift();
        }

        Veri.Alert.alertInTransitionHiding = false;
        Veri.Alert.alertInTransition = false;

        // Clear teh Timer
        if (this.alertTimerID > 0){
            // Clear Old Timer
            clearTimeout(Veri.Alert.alertTimerID);
            Veri.Alert.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
        Veri.Alert.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(){ Veri.Alert.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;

        // Make sure the alerts will be visible
        $("#alertSmall").css("z-index: 1000000");
 
        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(){ Veri.Alert.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",0);
        $("#alertSmall").css("width",$(document).width()-4);
        $("#alertSmall").fadeIn(js_fade_speed, function(){  
            // Show the alert for a minimum of minDisplayTime milliseconds
            setTimeout(function(){ Veri.Alert.alertInTransition = false; }, minDisplayTime); 
        } );
    }

}
// End Veri.Alert

// Hide All Alerts When The Page Is Scrolled
$(window).scroll(function () { 
    Veri.Alert.hideAlertNow();
});


/***
 **
 ** begin: LOGIN/SIGNUP FUNCTIONALITY
 **
 ***/

// Flag to determine if we've sent a reminder yet:
var reminderSent = 0;

// Initialise our invalid fields so user can't submit data until all 
// fields are verified valid (by default set them all to invalid)
var lsp_invalidFields = new Array();

// Callback function called on on login complete
// this is used to prompt the user to login then perform some action
// directly afterwords
var lsp_callback = '';

// Callback function called on successful activation
var s2p_callback = '';

// We have tried to confirm. The confirmation code goes in here if we need to store it client 
// side for some reason
var confirmation_code = false;
var confirmation_email = false;


// If we are loading (show/hide loading), it's good to keep track.
var is_loading = false;

/*
 * This is just a little function that check if the user is valid and confirmed
 *  and alerts the user to the problems
 *
 * return: boolean (true is valid, false if not)
 */

function is_user_confirmed()
{
	if (!USER.isLoggedIn) {
		Veri.Alert.triggerAlert('error', 'You must log in.');
		return false;
	}
	if (!USER.confirmed) {
		Veri.Alert.triggerAlert('error', 'You have not yet confirmed. <a onclick="sendActivationEmail(\''+USER.email+'\', sendActivationEmailComplete); return false;" href="javascript: void(0)">Click here</a> to resend activation email');
		return false;
	}
	return true;
}


/*
 *  jqmOpenLsp() - callback for after the lsp popup is shown
 */
function jqmOpenLsp(hash)
{
    jqmOpen(hash, function() {
        // Set focus to email field if it exists
        if(lsp_focusSignup == 1 && $('#lsp_email')[0].offsetHeight != 0){ // bug in jquery 1.1.4 (focus on hidden=bad)
            // DO NOTHING, DONT WANT TO REMOVE HINT: setTimeout('$("#lsp_email").focus()' , 500);
        }
        // Set focus to login username field if it exists
        else if($('#lsp_loginUsername')[0].offsetHeight != 0){ // bug in jquery 1.1.4 (focus on hidden=bad)
            setTimeout('$("#lsp_loginUsername").focus()' , 500);
        }
        lsp_focusSignup = 0;
    });
}
var lsp_focusSignup = 0;
/*
 *  lsp_show() - Initialize and show the Login / Signup Popup
 *
 *  @param callback - function to call upon successful login or signup
 */
function lsp_show(callback, focusSignup)
{

    pageTracker._trackPageview('/popup/login_signup/');

    // User is already confirmed
    if (USER.isLoggedIn == 1){
        Veri.Alert.triggerAlert('error','You are already logged in as "'+USER.username+'".');
        return;
    }

    // Reset Errors & Values
    // Search through all fields and clear all the errors
    // (assumes div ID ...._error)
    $("#loginSignupPop").find("input").not('#recaptcha_response_field').each(function(intIndex){
        // fieldId_error => fieldId
        var tempFieldId = $(this).attr("id");
        // clear error if it exists
        lsp_clearFieldError(tempFieldId);
        // Clear text if its a text input field
        if ($("#"+tempFieldId).attr('type') == 'text' || $("#"+tempFieldId).attr('type') == "password"){
            // clear the value
            $("#"+tempFieldId).val('');
        }
    });

    // All fields invalid
    lsp_invalidFields['lsp_username'] = 1;
    lsp_invalidFields['lsp_password'] = 1;
    lsp_invalidFields['lsp_email'] = 1;

    // Enable the submit buttons
    // (should be already by just incase)
    enableStyledButton('lsp_submit');
    enableStyledButton('lsp_loginSubmit');

    // Is there a callback set?
    if (typeof(callback) == "function"){
        // Make callback global so we can call it
        // later after signup or login completes
        lsp_callback = callback;
    }
    // No callback set
    else {
        // clear the callback in case it was set 
        // from a previous call
        lsp_callback ='';
    }

    CaptchaImage.newCaptcha('lsp_captchaImage', function(){
                           Veri.Alert.hideAlert();
                           // show the window
                           $("#loginSignupPop").jqmShow(); }
                    );

    if (typeof focusSignup != "undefined" && focusSignup == 1){
        lsp_focusSignup = 1;
    }

}

function lsp_showFromBanner(){
   pageTracker._trackPageview('/event/personalize_banner/');
   lsp_show(null, 1);
}

function lsp_forgotPassword()
{
    $('#loginSignupPop').jqmHide(fpp_show);
}

/*
 * lsp_verifyField() - verifies a form field via ajax and 
 *                     displays an appropriate error
 * 
 * @param fieldId - field to verify
 */
function lsp_verifyField(fieldId, callback)
{
    callback = (typeof(callback) == 'function') ? callback : function() {};

    // Get the value of hte field
    var fieldValue = $("#"+fieldId).val();

    // Return an error for blank fields
    if (fieldValue.length==0){
        var message = "";
        if (fieldId == "lsp_email"){
            message = "please enter your email address";
        }
        else if (fieldId == "lsp_username"){
            message = "please enter your display name";
        }
        else if (fieldId == "lsp_password"){
            message = "please fill in both password fields";
        }
        lsp_setFieldError(fieldId, message);
        return;
    }
 
    // Generate a random number to prevent caching:
    var rnd  = Math.round(Math.random()*1000000);

    $.ajax({
        url: base_url + '/prefservice/validateUserField/' + rnd,
        data: 'field='  + fieldId.substring(4,fieldId.length).toLowerCase()
            + '&value=' + fieldValue,
        dataType: 'xml',
        complete: function(xml)
        {
            var error_message  = '';

            // XML error
            if (!xml.responseXML){
                // Software Error
            }
            else{

                // Fetch the XML result code (0 or 1) from our response (e.g. <result>0</result>):
                var result   = $(xml.responseXML).find("result").text();

                // Grab the error message:
                // Field is Bad
                if (result == 0){
                    error_message = $(xml.responseXML).find("error").text();
                    // Update the field with the error:
                    lsp_setFieldError(fieldId,error_message);
                }
                // Field Is Good
                else if (result == 1){

                    // Field Good => Clear the Error
                    lsp_clearFieldError(fieldId);

                }
                // Result not != 1 or 0
                else {
                    // Software Error
                }        
                callback(fieldId, result);
            }
        }
    });
}

/**
 *
 * lsp_setFieldError() - Update the approriate field with an error message.
 *
 * @param string $fieldId - The name of the form field we need to add the error to
 *                          e.g. lsp_username
 * @param string $message The error message to display
 *
 */
function lsp_setFieldError(fieldId,message)
{

    // Mark field invalid
    lsp_invalidFields[fieldId] = 1;

    // confirm password errors go in the password error field
    if (fieldId == 'lsp_confirmPassword'){
        fieldId = 'lsp_password';
    }

    // Insert the error message
    $("#"+fieldId+"_error").html(ellipse(message,75));

    // Show the error message, if its not currently visible
    if (!$("#"+fieldId+"_error").is(":visible")){
        // If there is a hint message
	if ($("#"+fieldId+"_hint").length && $("#"+fieldId+"_hint").is(":visible")){
            // Slie Hint Up, Slide Error Down
            $("#"+fieldId+"_hint").slideUp(js_slide_speed);
            $("#"+fieldId+"_error").slideDown(js_slide_speed);
        }
        // No hint message
	else {
            // Slie the error in
            $("#"+fieldId+"_error").slideDown(js_slide_speed);
        }
    }

    // Make the field show an error
    if ($("#"+fieldId).length && !$("#"+fieldId).is(".inputError")){
        $("#"+fieldId).addClass('inputError');
    }
    // Show an error for confirm password too
    if (fieldId == 'lsp_password' && !$("#lsp_confirmPassword").is(".inputError")){
        $("#lsp_confirmPassword").addClass('inputError');
    }

}


/**
 *
 * lsp_clearFieldError() - Clear the error message associated with a field
 *
 * @param string $fieldId - The name of the form field we need to add the error to
 *                          e.g. lsp_username
 */
function lsp_clearFieldError(fieldId)
{

    // Mark field valid (!invalid)
    lsp_invalidFields[fieldId] = 0;

    // confirm password errors go in the password error field
    if (fieldId == 'lsp_confirmPassword'){
        fieldId = 'lsp_password';
    }

    // Hide the error message if its visible
    if ($("#"+fieldId+"_error").is(":visible")){
        // If there is a hint message
        if ($("#"+fieldId+"_hint").length){
            // Slie Error Up, Slide Hint Down
            $("#"+fieldId+"_error").slideUp(js_slide_speed);
            $("#"+fieldId+"_hint").slideDown(js_slide_speed);
        }
        // No hint message
        else {
            // Slie the error out
            $("#"+fieldId+"_error").slideUp(js_slide_speed);
        }
    }

    // Make the	field NOT show an error
    if ($("#"+fieldId).length && $("#"+fieldId).is(".inputError")){
        $("#"+fieldId).removeClass('inputError');
    }
    // Make the confirm password NOT show an error too
    if (fieldId	== 'lsp_password' && $("#lsp_confirmPassword").is(".inputError")){
      	$("#lsp_confirmPassword").removeClass('inputError');
    }

    // Keep the submit error?
    var keepSubmitMessage = 0;
 
    // If there are any invalid fields
    for (tempFieldId in lsp_invalidFields){
        // Disable submit
        if(lsp_invalidFields[tempFieldId] == 1){
             keepSubmitMessage = 1;
        }
    } 

    // Hide hte submit error if all fields = GOOD and error is visible
    if (!keepSubmitMessage && $("#lsp_submit_error").is(":visible")){
            $("#lsp_submit_error").slideUp(js_slide_speed);
    }

}


/*
 * doLogout() - Logs the user out
 *
 * @param confirmLogout - string, if set, will prompt the user 
 *                        with the value of the string to confirm 
 *                        they want to logout
 */
function doLogout(confirmLogout)
{

    // If confirmLogout is set
    if (typeof(confirmLogout)!="undefined" && confirmLogout != ''){
        if (!confirm(confirmLogout)){
            return;
        }
    }

    // Call our loginservice controller to log us out: (random number prevents caching)
    var rnd  = Math.round(Math.random()*1000000);

    clearTimeout(UserCookieListener.timeoutPointer);

    _show_main_loading_message();

    $.ajax({
        type: "POST",
        url: base_url + '/loginservice/logout/' + rnd,
        complete: function(response) {
            UserCookieListener.testCookie();
            // Set USER Login status
            refreshUserInfo(false);

            // Trigger Alert
            Veri.Alert.triggerAlert('success','You have now been logged out.');

            if (typeof(BetaTest) != 'undefined') {
                BetaTest.refresh();
            } else {
                // Call the data referesh handler (written customer for each view)
                // this refeshes the data that is effected by login/logout status
            //    _log_in_out_data_refesh();
         
            }
        }
    });
}

/**
 * hideAccountLinks() - hide the 'logout' and 'settings' in the header and show
 *                      the 'login' and 'signup' links.
 */
function hideAccountLinks() {
    $('#accountLink').fadeOut(js_fade_speed, function(){
        $('#welcomeLink').fadeIn(js_fade_speed);
    });
    Veri.Personalization.updateButtonDisplay(false);
}

/**
 * showAccountLinks() - show the 'logout' and 'settings' in the header and hide 
 *                      the 'login' and 'signup' links.
 */
function showAccountLinks() {
    $('#welcomeLink').fadeOut(js_fade_speed,function() {
        $('#accountLink').fadeIn(js_fade_speed);
    });
    Veri.Personalization.updateButtonDisplay(USER.username);
}

/**
 * lsp_doLogin() - Login Signup Popup: Logs the user into veritocracy
 *                 from the Login Signup Popup 
 */
function lsp_doLogin(){

    // Check for errors (use if else because we can only show one error at a time)
    if ($("#lsp_loginUsername").val().length==0){
        // Show error
        Veri.Alert.triggerAlert("warn","Please enter your email address in the email address field");
        return;
    }
    else if ($("#lsp_loginPassword").val().length==0){
        // Show error
        Veri.Alert.triggerAlert("warn","Please enter your password in the password field");
        return;
    }

    // Disable Submit So User Cant Hit It Twice
    disableStyledButton('lsp_loginSubmit');

    // Callback called when doLogin finishes
    var lsp_loginCallback = function(status){
        // Re-Enable Submit
        enableStyledButton('lsp_loginSubmit');

        // Don't refresh if the callback returns false
        var doRefresh = true;

        // Is there a login callback set?
        if (typeof(lsp_callback) == "function"){
            doRefresh = lsp_callback(); // call it
            lsp_callback = ''; // clear it
        }

        return doRefresh;
    };

    // Process Login 
    doLogin($("#lsp_loginUsername").val(),
            $("#lsp_loginPassword").val(),
            $("#lsp_loginRememberme").is(":checked"),
            lsp_loginCallback);
}


/**
 * doLogin() - Logs the user into veritocracy.
 *                 
 * @param user - username
 * @param pass - password
 * @param remember - remember me?
 * @param callback - function called on complete, passed "int result" as argument
 */
function doLogin(user,pass,remember,callback)
{

    // Status text while we wait for the AJAX request to complete:
    var wtx = 'Processing login, please wait...';

    // Show Logging In Alert
    Veri.Alert.triggerAlert('loading',wtx, 7000);

    // Generate a Random Number
    var rnd  = Math.round(Math.random()*1000000);

    clearTimeout(UserCookieListener.timeoutPointer);

    if (typeof(remember) != "undefined" && remember){
        remember = 1;
    }
    else {
        remember = 0;
    }

    // Launch a new AJAX request with the username and password (loginComplete() will handle the rest):
    $.ajax({ type: "POST",
             url:  base_url + '/loginservice/login/' + rnd,
             data: 'user='+user+'&pass='+pass+'&remember='+remember 
                   + ( (confirmation_code && confirmation_email) ? '&activate_code='+confirmation_code+'&activate_email='+confirmation_email : '' ),
             dataType: 'xml',
             complete: function(xml){ 

                 // Check the XML result code and react accordingly:
                 // Forced Success Response
                 if (typeof(xml) == "number" && xml == 1){
                     var result = 1;
                 }
                 // Else Parse XML
                 else {
                     // Fetch the XML result code (-1, 0, 1 or 2) from our response (e.g. <result>0</result>):
                     var result   = $(xml.responseXML).find("result").text();
                 }

                 // Hide Loading Message
                 // (do this here because login complete should not always do it)
                 if ((typeof(BetaTest) == 'undefined') && ( result > 1) )
                     Veri.Alert.hideAlert();

                 // We''ve logged in successfully
                 if (result > 0){
                     // If we''re in a DHTML popup, fade out the form and fade in a status message:
                     $('#loginSignupPop').jqmHide();

                     var confirmed = 1;
                     if (result==2){
                         confirmed = 0;
                     }

                     // If we are logging in after trying to confirm and requiring a login
                     if (confirmation_code){
                         if (confirmed)
                             Veri.Alert.triggerAlert('success', 'You Have Been Confirmed!');
                         else
                             Veri.Alert.triggerAlert('warning', 'Confirmation Code was incorrect.');

                         confirmation_code = false;
                         confirmation_email = false;
                     }
 
                     // Update the display and state variables
                     loginComplete($(xml.responseXML).find("id").text(),
                                   $(xml.responseXML).find("displayname").text(),
                                   $(xml.responseXML).find("email").text(),
                                   confirmed,
                                   $(xml.responseXML).find("unread_message_count").text());

                 } else {
                     var errorMessage = (result == -1)
                                      ? 'This account has been deleted'
                                      : 'Invalid username / password';

                     // Show error
                     Veri.Alert.triggerAlert("error", errorMessage);

                     // Clear The passowrd
                     $("#lsp_loginPassword").val("");

                     $("#lsp_loginUsername").val(user);

                 }

                 // Don''t refresh if the callback returns false 
                 var doRefresh = true;
                 if (typeof(callback) == "function"){
                     // Callback function
                     doRefresh = callback(result);
                 }


                 // Call the data referesh handler (written customer for each view)
                 // this refeshes the data that is effected by login/logout status
                 if ((result > 0) && (doRefresh !== false)) {
                     _log_in_out_data_refesh();
                 }

                 // Restart login/logout cookie listener
                 UserCookieListener.testCookie();
             } 
    });

}


/**
 *
 * loginComplete() - Callback function to handle successful login
 *
 * @param string displayName - username of user
 * @param string email - email of user
 * @param string confirmed - is the user confirmed? 
 * @param bool silent - should hte user be notified they are now logged in?
 */
function loginComplete(id,displayName,email,confirmed,unreadMessages,silent)
{

        pageTracker._trackPageview('/action/login/');

        if (typeof(silent) == 'undefined'){
             silent = 0;
        }

        // Set User Info
        refreshUserInfo({'id'          : id,
                         'displayName' : displayName,
                         'email'       : email,
                         'confirmed'   : confirmed});

        if(!silent){

            // Trigger Alert

            if (typeof(BetaTest) == 'undefined'){
                Veri.Alert.triggerAlert('success','Welcome '+USER.username+', you are now logged in.');
            }

       }

}

/**
 *
 * lsp_processSignup() - Login Signup Popup: Processes the first stage 
 *                       of the veritocracy signup process.
 *
 */
function lsp_processSignup()
{
    // Check that all fields have been verified
    var readyToSubmit = 1;

    var fieldsToVerify = 0;
    // If there are any invalid fields
    for (tempFieldId in lsp_invalidFields) {
        // Invalid Field Found
        if (lsp_invalidFields[tempFieldId] == 1) {

            // don't submit until all fields have been verified
            fieldsToVerify++;

            // Verify the field there will be an error message
            lsp_verifyField(tempFieldId, function(fieldId, result) {
                if ((result == 1) && fieldsToVerify != -1) {
                    if (!(--fieldsToVerify)) {
                        lsp_processSignup();
                    }
                } else {
                    // failed
                    fieldsToVerify = -1;
                }
            });
            // Not ready to submit
            readyToSubmit = 0;
        }
    }

    if (Recaptcha.get_response() == '' || $('#recaptcha_response_field').is('.captchaResponseEmpty')) {
        Veri.Alert.triggerAlert('warn', 'Please copy the text you see in the image into the box below it');
        readyToSubmit = 0;
    }
   

    // Not ready to submit
    if (!readyToSubmit){

        // Show error if its not already visible
        if(!$("#lsp_submit_error").is(":visible")){
            $("#lsp_submit_error").slideDown(js_slide_speed);
        }
        return; // Dont Submit
    }

    // Otherwise we're ready to submit

    // Disable the Submit Button while we process
    disableStyledButton('lsp_submit');

    // Prepare/fade in new status popup while we wait for the Ajax request to complete:
    Veri.Alert.triggerAlert('loading','Processing signup, please wait...');

    // Grab all our POST data from the form, and generate a random number:
    var user  = $("#lsp_username").val();
    var pass  = $("#lsp_password").val();

    var email = $("#lsp_email").val();
    var rnd   = Math.round(Math.random()*1000000);

    // Launch a new AJAX request with the neccessary POST data (lsp_signupComplete() will handle the rest):
    
    var betaCodeParam 
        = (typeof(BetaTest)!='undefined') ? '&referrer_code=' + $("#have_code").val() :'';

    $.ajax({ type: "POST",
             url:  base_url + '/regservice/signup1/' + rnd,
             error: function (param1,param2,param3) {
                 // Enable teh signup button
                 enableStyledButton('lsp_submit');
                 // Call Global Handler
                 AjaxHandler.handleError.call(this,param1,param2,param3);
             },
             data: 'username=' + user 
                          + '&password=' + pass 
                          + '&email=' + email  
                          + '&captchaId=' + Recaptcha.get_challenge()
                          + '&captchaAnswer=' + Recaptcha.get_response()
                          + betaCodeParam,
             dataType: 'xml',
             complete: function(xml){ 
                 var result   = $(xml.responseXML).find("result").text();
                 lsp_signupComplete(xml,user,email,pass); 
             } });
}

/**
 * lsp_signupComplete() - Login Signup Popup: Callback function to handle AJAX 
 *                        reponse from lsp_processSignup().
 *
 * @param string $xml     The xml response doc
 * @param string $user    The username we're trying to register.
 * @param string $email   The email address we are using to register with.
 * @param string $pass    The password we chose for our username above.
 *
 */
function lsp_signupComplete(xml,user,email,pass)
{

    // Fetch the XML result code (0 or 1) from our response (e.g. <result>0</result>):
    var response = xml.responseXML.documentElement;
    var result   = response.getElementsByTagName('result')[0].firstChild.data;

    // Hide Any Loading Messages
    Veri.Alert.hideAlert();

    // Check if the registration was successful or not:
    if (result > 0) {

        pageTracker._trackPageview('/action/signup_complete/');

        // Hide the signup window
        $("#loginSignupPop").jqmHide();

        // Throw Sucess Message
        Veri.Alert.triggerAlert("success","Account '"+email+"' created successfully! You are now logged in.");

        // User is logged in (upon signup), Update Display
        loginComplete($('response > id', xml.responseXML).text(),user,email,0,0,1);

        // Don't refresh if the callback returns false
        var doRefresh = true;

        // Is there a login callback set?
        if (typeof(lsp_callback) == "function"){
            doRefresh = lsp_callback(); // call it
            lsp_callback = ''; // clear it
        }

        if (typeof(BetaTest) != 'undefined') BetaTest.refresh();
        else { Kickstart.build(true); } // do kickstart
        return doRefresh;

    }else{

        // If we were not successful (result code 0), read the list of errors from our XML response:
        var tgs = response.getElementsByTagName('error');

        // Enable teh signup button
        enableStyledButton('lsp_submit');

        // Loop over each error from teh server and display them
        for (i=0;i<tgs.length;i++){
            var errorMessage = response.getElementsByTagName('error')[i].firstChild.data;
            var field = response.getElementsByTagName('error')[i].getAttribute("field");
            if(field == 'captcha')
            {
               Veri.Alert.triggerAlert('error', 'Incorrect words entered for "Prove You Are Human"');
            }
            else
               lsp_setFieldError("lsp_"+field, errorMessage);
        }

        CaptchaImage.reload();

        // Show general submit error if its not already visible
        if(!$("#lsp_submit_error").is(":visible")){
            $("#lsp_submit_error").slideDown(js_slide_speed);
        }

    }   
}

/**
 *
 * sendActivationEmail() - Send the Activation Email
 *
 * @param email - email address
 * @param callback - callback function called on complete
 *
 */
function sendActivationEmail(email, callback)
{

    // Generate a random number used to prevent caching:
    var rnd = Math.round(Math.random()*1000000);

    // Launch a new AJAX request with the neccessary POST data (resendComplete() will handle the rest):
    $.ajax({ type: "POST",
             url:  base_url + '/regservice/resendCode/' + rnd,
             data: 'email=' + email + '&username=' + USER.username,
             dataType: 'xml',
             complete: function(xml){ if (typeof(callback) == "function") { callback(xml,email,callback); } } });
          
}


/**
 *
 * sendActivationEmailComplete() - alerts the user that the email has been sent
 *
 */

function sendActivationEmailComplete(xml, email, callback)
{

    var response = xml.responseXML.documentElement;
    var result   = response.getElementsByTagName('result')[0].firstChild.data;
    Veri.Alert.hideAlert();
    if (result > 0) {
        Veri.Alert.triggerAlert('success', 'Activation resent! Please check your email.');
    }
}


/**
 * refreshUserInfo()
 *
 * Refreshes values of the USER object and shows the approporiate buttons in the header.
 *
 * @param Object userInfo - associative array or false for no user logged in.
 */
function refreshUserInfo(userInfo)
{
    if (userInfo) { // logged in
        USER.isLoggedIn = 1;
        USER.id         = userInfo['id'];
        USER.username   = userInfo['displayName'];
        USER.email      = userInfo['email'];
        USER.confirmed  = userInfo['confirmed'];
        updateUnreadMessageCount(userInfo['unreadMessages']);
        showAccountLinks();
    } else { // logged out
        USER.isLoggedIn  = 0;
        USER.id          = 0;
        USER.username    = '';
        USER.email       = '';
        USER.confirmed   = -1;
        updateUnreadMessageCount(); // no unread messages
        hideAccountLinks();
    }
}

/**
 *  fpp_show() - show forgot password popup
 */
function fpp_show()
{
    Veri.Alert.hideAlert();
    Veri.Alert.triggerAlert('loading' , 'Loading Captcha Image');

    $('#fpp_email').val('');

    CaptchaImage.newCaptcha('fpp_captchaImage', function() {
        Veri.Alert.hideAlert();
        $('#forgotPasswordPop').jqmShow();
    });
}

/**
 *  fpp_resetPassword()
 */
function fpp_resetPassword()
{

     // Blank Email
     if($("#fpp_email").val().length < 4){
         Veri.Alert.triggerAlert('error','Please enter your email address');
         return 0;
     }

    else if (Recaptcha.get_response() == ''
             || $('#recaptcha_response_field').is('.captchaResponseEmpty')) {
        Veri.Alert.triggerAlert('warn',
                'Please copy the text you see in the image into the box below it');
        return 0;
    }

     Veri.Alert.triggerAlert('loading' , 'Processing...');

     // Submit reset password request
     $.ajax({'url'     : '/loginservice/resetPassword/',
             'type'    : "POST",
             'data'    : 'email='+$("#fpp_email").val()
                       + '&captchaId=' + Recaptcha.get_challenge()
                       + '&captchaAnswer=' + Recaptcha.get_response(),
      	     'complete': function(response, status) {

                 if (status != 'success') {
                     Recaptcha.reload();
                     return;
                 }

                 var xml = response.responseXML;

                 // Hide Loading Alert
                 Veri.Alert.hideAlert();

                 // Success
                 if ($('result', xml).text().length && 
                     $('result', xml).text() > 0)
                 {
                     Veri.Alert.triggerAlert('success','Success. Please check your email.'); 
                     $("#forgotPasswordPop").jqmHide();
                 } else {
                     Recaptcha.reload();
                     Veri.Alert.triggerAlert('error',$('error', xml).text());
                 }

            }});

}


/**
 * UserCookieListener
 *
 * Handles detection of login/logout for this session in other windows.  
 */
var UserCookieListener = {
    timeout:        3000,
    timeoutPointer: 0,

    /*
     * testCookie()
     *
     * On login/logout, server sets/unsets a cookie containing the user id
     * of the currently logged in user.  This function tests the cookie's value
     * against the user data. (the USER object)
     *
     * This function is a listener, it calls itself via setTimeout.
     *
     * @returns boolean - true if we refreshed, false otherwise.
     */
    testCookie: function()
    {

        // If abort, return
        if (document.cookie.match(/USERLOGGEDIN=ABORT/)){
            return;
        }
        // Else Get Cookie Value       
        var loggedInCookie = document.cookie.match(/USERLOGGEDIN=([^;]+)/);

        loggedInCookie = (loggedInCookie)
                       ? unescape(loggedInCookie[1].replace(/\+/g, ' ')) : false;

        // Does the cookie value match our data?
        var doRefresh = (loggedInCookie != ((USER.isLoggedIn) ? USER.id: false));

        var doRecurse = true;  // this will be set to false if we need to halt the timer

        if (doRefresh) {

            if (!loggedInCookie) { // logging out

                refreshUserInfo(false);
            } else {               // logging in

                doRecurse = false; // halt this timer until ajax data comes back

                // get the new user's info
                $.ajax({'url'     : '/loginservice/getUserData/',
                        'complete': function(response, errorName) {
                            var xml = response.responseXML;
                            if ((errorName == 'success') && ($('result', xml).text() > 0)) {
                                refreshUserInfo(
                                        {'displayName': $('displayname', xml).text(),
                                         'email'      : $('email', xml).text(),
                                         'id'         : $('id', xml).text(),
                                         'confirmed'  : $('result', xml).text() & 1 });

                                // our data should be in synch now
                                if (USER.id != loggedInCookie) { // shouldn't happen
                                    UserCookieListener.handleFailiure(loggedInCookie);
                                }
                            } else { // ajax error
                                UserCookieListener.handleFailiure(loggedInCookie);
                            }
                            // restart timer
                            UserCookieListener.testCookie();
                        }});

            }

            if (!USER.isLoggedIn && typeof(BetaTest) != 'undefined') {
                // redirect to beta login page if it's beta and logout
                BetaTest.refresh();
                return;
            }

            _log_in_out_data_refesh(!USER.isLoggedIn);
        }

        if (doRecurse) {
            UserCookieListener.timeoutPointer =
                    setTimeout(UserCookieListener.testCookie,
                               UserCookieListener.timeout);
        }

        return doRefresh;
    },

    /**
     * handleFailiure()
     *
     * This is a fail-safe mechanism.  If there is an issue with synchronisation of the
     * user data and the cookie, we have to destroy the listener so that it doesn't try
     * to load ajax data on every call!
     */
    handleFailiure: function(cookieValue)
    {
        UserCookieListener.testCookie = function() {return false;};

        document.cookie = 'USERLOGGEDIN=ABORT;domain='
                           + base_url.replace(/^http:\/\//, '') + ';';

        if (DEBUG_MODE && console) {
            console.debug('Cookie listener aborted!  Cookie: "' + cookieValue +
                                      '"; User Id: "' + USER.id + '"');
        }
    }

} // END NAMESPACE UserCookieListener


/**
 * CaptchaImage
 *
 * Handles loading and displaying of captcha images.
 */
CaptchaImage = {
    isActive: false,

    /**
     * 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) {

        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"'
        + ' onfocus="CaptchaImage.hideResponseText();"'
        + ' onblur="CaptchaImage.showResponseText();"'
        + ' class="text_input" />'
        + '<div id="captcha_hint" class="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(CaptchaConfig.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.v0.gif)");
    },

    /**
     * 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.v0.gif)");

    },

    /**
     * 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;
    }
}


Veri.generateSessionKey = function()
{
    var matches = document.cookie.match(/PHPSESSID=(\w+)(;|$)/);

    if (!matches) { return false; }

    return getMd5(matches[1]);
};

/***
 **
 ** begin: VOTE FUNCTIONALITY
 **
 ***/

// Create new Veri.Vote Namespace
if (Veri.Vote == null || typeof(Veri.Vote) != "object") { Veri.Vote = new Object(); }

// Begin Definition
Veri.Vote = {

    /*
     * Enable Interactivity
     */
    enableInteractivity : function(){

        // Mouse Over For Vote Icons
        $("a.up").mouseover(function(){
            $(this).addClass("over");
        }).mouseout(function(){
            $(this).removeClass("over");
        });
        $("a.down").mouseover(function(){
            $(this).addClass("over");
        }).mouseout(function(){
            $(this).removeClass("over");
        });

    },

    /*
     *  Cast a vote
     */
    castVote : function(topic,id,vote)
    { 
        var sessionKey;
        // If the user is logged in, cast a vote
        if (USER.isLoggedIn && (sessionKey = Veri.generateSessionKey())) {

            pageTracker._trackPageview('/action/vote/');

            // Process The Vote On The Server
            $.ajax({ type: "POST",
                     url:  base_url + '/voteService/addVote/',
                     data: 'topic=' + topic + '&opinion=' + id + '&vote=' + vote + '&key=' + sessionKey,
                     dataType: 'xml',
                     handleNotLoggedIn: function(){ Veri.Vote.castVote(topic, id, vote); },
                     complete: function(http,type){ 
                          Veri.Vote.voteComplete(http,type,id,vote);
                     }}); 

            // Update the display immediately

            // Current State = No Vote
            if (!$(".vote_up_"+topic+"_"+id).hasClass('active') && !$(".vote_down_"+topic+"_"+id).hasClass('active')){

                // New Up Vote
                if (vote == 'up'){
                    // Show Up Vote
                    $(".vote_up_"+topic+"_"+id).addClass('active');
                    Veri.Alert.triggerAlert("success","UP vote sucessfully submitted!");
                }
                // New Down Vote
                if (vote == 'down'){
                    // Show Up Vote
                    $(".vote_down_"+topic+"_"+id).addClass('active');
                    Veri.Alert.triggerAlert("success","DOWN vote sucessfully submitted!");
                }
            }
            // Current State = Up Vote
            else if ($(".vote_up_"+topic+"_"+id).hasClass('active')){

                // New Up Vote
                if (vote == 'up'){
                    // Hide Up Vote
                    $(".vote_up_"+topic+"_"+id).removeClass('active');
                    Veri.Alert.triggerAlert("success","UP vote sucessfully removed!");
                }
                // New Down Vote
                if (vote == 'down'){
                    // Hide Up Vote
                    $(".vote_up_"+topic+"_"+id).removeClass('active');
                    // Show Down Vote
                    $(".vote_down_"+topic+"_"+id).addClass('active');
                    Veri.Alert.triggerAlert("success","UP vote sucessfully changed to DOWN vote!");
                }

            }
            // Current State = Down Vote
            else if ($(".vote_down_"+topic+"_"+id).hasClass('active')){

            // New Up Vote
            if (vote == 'up'){
                    // Hide Down Vote
                    $(".vote_down_"+topic+"_"+id).removeClass('active');
                    // Show Up Vote
                    $(".vote_up_"+topic+"_"+id).addClass('active');
                    Veri.Alert.triggerAlert("success","DOWN vote sucessfully changed to UP vote!");
                }
                // New Down Vote
                if (vote == 'down'){
                    // Hide Down Vote
                    $(".vote_down_"+topic+"_"+id).removeClass('active');
                    Veri.Alert.triggerAlert("success","DOWN vote sucessfully removed!");
                }

            }


        }
        // User is not logged in
        else {

            pageTracker._trackPageview('/action/vote_not_loggedin/');

            // Warn the user
            Veri.Alert.triggerAlert("warn","Please log in so we can learn from your votes and personalize Veri for you.");
            // Call back to cast vote after user successfully 
            // logs in or signs up
            var castVoteCallback = function(){
                Veri.Vote.castVote(topic,id,vote);
            }
            // Login or signup => Then Cast Vote
            lsp_show(castVoteCallback);
        }

    },


    /*
     *  Handles response from server after casting a vote
     */
    voteComplete : function(http,type,id,vote)
    {

        // Fetch the XML result code (0 or 1) from our response (e.g. <result>0</result>):
        var response = http.responseXML.documentElement;
        var result   = response.getElementsByTagName('result')[0].firstChild.data;

        // Grab the error message if voting failed:
        if (result == 0 && type != 'success'){
            var txt = response.getElementsByTagName('error')[0].firstChild.data;
            if (!txt){
                txt = 'There was an error contacting the server';
            }
            Veri.Alert.triggerAlert("error","Voting failed: " + txt);
        }
        // Voting Succeeded => Nothing else to do 

    },

 
    /*
     *  Cast Up Vote
     */
    voteUp : function(topic,id)
    {
        Veri.Vote.castVote(topic,id,'up');
    },


    /*
     *  Cast Down Vote
     */
    voteDown : function(topic,id)
    {
        Veri.Vote.castVote(topic,id,'down');
    }


}
// End Veri.Vote Namespace

// Create new Veri.Personalization namespace
if(Veri.Personalization == null || typeof(Veri.Personalization != 'object')) { Veri.Personalization = new Object(); }

Veri.Personalization = {

    timeout : 2000,
    processing : false,
    instance : null,
    
    // re-initialize the mouseovers
    reinitialize : function()
    {
        this.processing = false;
        $("#nav_personalized").bind('mouseover', 
            function(){
                $(this).addClass('over');
            }
        );
    },

    togglePersonalization : function(categoryId)
    {

        if (!USER.isLoggedIn) {

            // Warn the user
            Veri.Alert.triggerAlert("warn","Please log in so we can learn from your votes and personalize Veri for you.");

            // Login or signup => Then Turn On Personalization
	    lsp_show();

            pageTracker._trackPageview('/action/toggle_personalization_login/');

            return;

        }

        if (is_loading) { return; }

        if(this.processing == false) {

            pageTracker._trackPageview('/action/toggle_personalization_complete/');

            this.processing = true;
            $("#nav_personalized").removeClass('over');
            $("#nav_personalized").unbind('mouseover');
            if($("#nav_personalized").hasClass("on")){
                $("#nav_personalized").removeClass('on').addClass('off');
            }else{
                $("#nav_personalized").removeClass('off').addClass('on');
            }

            _show_main_loading_message();
    
            $.ajax({type: "POST",
                url:  base_url + '/personalizationservice/togglePersonalization/',
                dataType: 'xml',
                complete: function(xml){
                    var preference = $(xml.responseXML).
                        find("preference").text();
                    var message = $(xml.responseXML).
                        find("message").text();

                    Veri.Personalization.reinitialize();
                    _log_in_out_data_refesh();
                }
            });
            //setTimeout("Veri.Personalization.reinitialize()", this.timeout);
        }
    },

    /**
     * updateButtonDisplay() - change display of personalization button when user
     *                         logs in or out.
     *
     * @param mixed loginUserName - name of user or false if it's logout
     */
    updateButtonDisplay: function(loginUserName)
    {
        if (loginUserName) { // user just logged in
            $('#nav_personalizedForUserName').html(loginUserName);
            $('#nav_personalized').removeClass('off').addClass('on');
        } else { // user just logged out
            $('#nav_personalizedForUserName').html('you');
            if ($('#nav_personalized').is('.on')) {
                $('#nav_personalized').removeClass('on').addClass('off');
            }
        }
    }
} 
// End Veri.Personalization namespace

/**
 *
 */
Veri.Topic = {

    inPopup: 0,

    // from model:
    topicId          : null,
    opinionHash      : null,
    topicIsApproved  : null,
    topicScrollSaveX : null,
    topicScrollSaveY : null,

    // returns the dom id prepended with topicPop_ if we're in the popup
    domId: function(id) { return ((Veri.Topic.inPopup) ? 'topicPop_' : '') + id; },

    toggleShare: function()
    {
            if($('#veri_shareLayer').is(":visible")){
                Veri.Topic.hideShare();
            }
            else {
                Veri.Topic.showShare();
            }
    },

    showShare: function()
    {

            // Hide the popup content, show the share layer
            $('#veri_iframe').hide();
            $('#popTopicContent').fadeOut(function(){
                $('#veri_shareLayer').fadeIn();
            });

    },

    hideShare: function()
    {
            // Hide the popup content, show the share layer
            $('#veri_shareLayer').fadeOut(function(){
                $('#veri_iframe').fadeIn();
                $('#popTopicContent').fadeIn();
            });
    },

    twitterShare: function()
    {
            window.open("http://twitter.com/home?status="+$('#veri_widget_share_twitter').val());
    },


    /**
     * view_opinion_with_params(hash)
     *
     * View Opinion With teh appropriate time period set.
     *
     * @param string opinion_hash - opinion hash.
     * @param string topic_hash - topic hash
     * @returns bool - ALWAYS FALSE so that href doesn't fire.
     */
    viewArticle: function(topic_hash, opinion_hash, el)
    {
        var period = HashListener.getCurrentOpt('_pd', true);

        if (period !== null) {
             var params = 'period=' + period;
        }

        return Veri.Article.view(topic_hash, opinion_hash, params, el);
    },

    /*
     * backToTopic()
     *
     * Navigates to topic page via POST
     *
     * @param bool resetScroll - if flag is set, scroll positions will not
     *                           be sent back to topic page.
     *
     * @returns bool - ALWAYS FALSE so that href doesn't fire.
     */
    backToTopic: function(resetScroll)
    {
        // If looking at an article in the popup => Just go to the url
        if (Veri.Article.inPopup()){
            return true;
        }

        if (!Veri.Topic.topicIsApproved) {
            Veri.Alert.triggerAlert('error', 'Cannot view an unapproved topic.');
            return false;
        }

        var hashStr = window.location.hash.replace(/^#[^\/]*/ , '#');

        window.location.href = location.pathname.match(/^(.*\/)[^\/]+\/?/)[1]
                             + hashStr;

        return false;
    },

    // Variables Set When Things Have Loaded
    topLinksLoaded    : 0,
    pollSumsLoaded    : 0,
    summListLoaded    : 0,
    opinPrevLoaded    : 0,
    shrTopicLoaded    : 0,

    chooseOption: function()
    {
        var option1, option2;

        option1 = document.getElementById(Veri.Topic.domId('multiPollRange1')).value;

        if (document && document.getElementById(Veri.Topic.domId('multiPollRange2'))) {

            option2 = document.getElementById(Veri.Topic.domId('multiPollRange2')).value;
        } else {
            option2 = null;
        }

        HashListener.rebuildHash({'_pg' : 1,
                                  '_o1' : option1,
                                  '_o2' : option2});
    },

    chooseRange: function()
    {
        var option1, option2;

        option1 = document.getElementById(Veri.Topic.domId('scalePollRange1')).value;

        if (document && document.getElementById(Veri.Topic.domId('scalePollRange2'))) {

           option2 = document.getElementById(Veri.Topic.domId('scalePollRange2')).value;
        } else {
           option2 = null;
        }

        HashListener.rebuildHash({'_pg' : 1,
                                  '_o1' : option1,
                                  '_o2' : option2});
    },


    loadTopic: function(opts, firstRun, refresh, isDefault, changed)
    {
        var sortBy = opts['_vw'];
        var page   = opts['_pg'];
        var unread = opts['_ur'];
        var prd    = opts['_pd'];
        var poll   = opts['_pl'];
        var op1    = opts['_o1'];
        var op2    = opts['_o2'];
        var qry    = opts['_qr'];

        // set value of query field (hash listener won't unescape it automatically)
        $("#" + Veri.Topic.domId("topicSearch")).val(decodeURIComponent(qry));

        if (firstRun) { // hide opinion pane; show topic pane
            $("#" + Veri.Topic.domId("opinionContent")).hide();
            $("#" + Veri.Topic.domId("topicContent")).show();

            // scroll to position saved from opinion page
            scrollTo(Veri.Topic.topicScrollSaveX , Veri.Topic.topicScrollSaveY);
        } else if (Veri.Topic.inPopup) {
            $('#popTopicContent').scrollTop(0);
        } else {
            scrollToTopSmooth();
        }

        // Decide Whether We Will Need to reload hte left column
        // Left column reload if period changes or we are refreshing
        var noleft = (!changed['_pd'] && !refresh);

        // Reload some stuff
        if (!firstRun || !isDefault){

            // Don't load whole page (w/left column)
            if (noleft) {
                showLoading(Veri.Topic.domId('opinionPreviews'));
            } else {
                // Load whole page (w/left column)
                   showLoading(Veri.Topic.domId('topicContent'), function() {
                       Veri.Topic.topLinksFaded     = 1;
                       Veri.Topic.pollSumsFaded     = 1;
                       Veri.Topic.shrTopicFaded     = 1;
                       Veri.Topic.topicContentFaded = 1;
                       Veri.Topic.opinPrevFaded     = 1;
                   });
            }
        } 


    /*
        // Make sure unread/all is correctly selected:
        var els = document.getElementsByName('unreadOnly');

        if (unread == 1) {
            els[0].checked = true;
        } else {
            els[1].checked = true;
        }
    */

        if (isDefault && firstRun) {
            // We already have evertying loaded.
            Veri.Topic.opinPrevLoaded = 1;

            if (!noleft) {
                Veri.Topic.pollSumsLoaded = 1;
                Veri.Topic.shrTopicLoaded = 1;
                Veri.Topic.topLinksLoaded = 1;
                Veri.Topic.summListLoaded = 1;
            }

            Veri.Topic.loadTopicComplete(noleft);

        } else {

            // Normal opinion previews or search results ?
            var opinionUrl = (qry != '') ? 'opinionPreviewsByQueryString' : 'opinionPreviews';

            // Load Opinion Previews
            $("#" + Veri.Topic.domId("opinionPreviews")).fillFromAjax(
                base_url + '/topicService/'+opinionUrl+'/?topic='   + Veri.Topic.topicId
                                                      + '&inPopup=' + Veri.Topic.inPopup
                                                      + "&view="    + sortBy
                                                      + "&poll="    + poll
                                                      + "&page="    + page
                                                      + "&unread="  + unread
                                                      + "&period="  + prd
                                                      + "&op1="     + op1
                                                      + "&op2="     + op2
                                                      + "&query="   + qry,
                function() {
                    Veri.Topic.opinPrevLoaded = 1;
                    Veri.Topic.loadTopicComplete(noleft);
                    Veri.Vote.enableInteractivity();
                },
                ((noleft) ? $.LOAD_FADED : 0)
            );

            // Do we load left hand column?
            if (!noleft){
                // Load Left Hand Column
                $("#" + Veri.Topic.domId("pollSummaries")).fillFromAjax(base_url
                                         + "/topicService/pollSummaries/?topic=" + Veri.Topic.topicId
                                                                    + '&inPopup=' + Veri.Topic.inPopup
                                                                      + "&poll=" + poll
                                                                      + "&view=" + sortBy
                                                                      + "&period=" + prd,
                              function(){
                                  Veri.Topic.pollSumsLoaded = 1;
                                  Veri.Topic.loadTopicComplete(noleft);
                });

                $("#" + Veri.Topic.domId("relatedTopics")).fillFromAjax(base_url
                                         + "/topicService/sharedTopics/?topic=" + Veri.Topic.topicId
                                                              + '&inPopup=' + Veri.Topic.inPopup
                                                              + "&view=top&period=" + prd,
                              function(){
                                  Veri.Topic.shrTopicLoaded = 1;
                                  Veri.Topic.loadTopicComplete(noleft);
                });

                $("#" + Veri.Topic.domId("topLinks")).fillFromAjax(base_url + "/topicService/topLinks/?topic=" + Veri.Topic.topicId
                                                                + '&inPopup=' + Veri.Topic.inPopup
                                                                + "&view=" + sortBy
                                                                + "&period=" + prd,
                              function(){
                                  Veri.Topic.topLinksLoaded = 1;
                                  Veri.Topic.loadTopicComplete(noleft);
                                });

                Veri.Topic.summListLoaded = 1;  
            } // END IF noleft
        } // END IF NOT isDefault AND firstRun
    }, // END FUNCTION loadTopic

    /*
     * Enabled the mouse over and click actions for poll summaries
     */
    enablePollSummaryInteractivity: function(){

        if (Veri.Topic.inPopup) {
            var pollId = Veri.ViewTopic.state.params['_pl'];
        } else {
            var pollId = HashListener.getCurrentOpt('_pl');
        }

        // Mouse Over Highlight And Click Actions For Poll Summaries
        $("#" + Veri.Topic.domId("pollSummaries")).children("div.pollSummary").each(function(){

            // If this is the current poll
            if (pollId && pollId.length && pollId == $(this).attr('id').strrstr('_').substr(1)){
                // Make this selected
                $(this).removeClass('unselected').addClass('selected');
                // Clear Actions
                $(this).unbind('mouseover').unbind('mouseout').unbind('click');
                // Enable rollover and onclick actions
                $(this).mouseover(function(){
                    $(this).addClass('active');
                }).mouseout(function(){
                    $(this).removeClass('active');
                }).click(function(){
                    // Go Back To Normal View
                    HashListener.rebuildHash({'_pg':1,'_pl':0,'_qr':''});
                    // Make UnSelected So It Happens Instantly
                    $(this).removeClass('selected').removeClass('active').addClass('unselected');
                    // Clear actions so no actions fire until transition is	complete
                    $(this).unbind('mouseover').unbind('mouseout').unbind('click');
                });

            }
            // This is not the current poll (or there is no currently selected poll)
            else {
                // Make this unselected
                $(this).removeClass('selected').addClass('unselected');
                // Clear Actions
                $(this).unbind('mouseover').unbind('mouseout').unbind('click');
                // Enable rollover and onclick actions
                $(this).mouseover(function(){
                    $(this).addClass('active');
                }).mouseout(function(){
                    $(this).removeClass('active');
                }).click(function(){
                    pollId = $(this).attr('id').strrstr('_').substr(1); // i.e. pollId=5 FROM pollSummary_5
                    // Go To View by Poll
                    HashListener.rebuildHash({'_pg':1,'_pl':pollId,'_qr':''});
                    // Make Selected So It Happens Instantly
                    $(this).removeClass('unselected').removeClass('active').addClass('selected');
                    // Clear actions so no actions fire until transition is complete
                    $(this).unbind('mouseover').unbind('mouseout').unbind('click');
                });
            }

        });
    },

    loadTopicComplete: function(noleft)
    {
        if (Veri.Topic.opinPrevLoaded == 1 &&
                (  noleft
                 || (   Veri.Topic.topLinksLoaded == 1
                     && Veri.Topic.pollSumsLoaded == 1
                     && Veri.Topic.shrTopicLoaded == 1
                     && Veri.Topic.summListLoaded == 1)))
        {
     
            // Update Poll Summary Highlights and Clicks
            Veri.Topic.enablePollSummaryInteractivity();
            
            // Don't load whole page (w/left column)
            if (noleft) {
                hideLoading(Veri.Topic.domId('opinionPreviews'));
            }
            // Load whole page (w/left column)
            else {
                hideLoading(Veri.Topic.domId('topicContent'));
                Veri.Topic.enablePollSummaryInteractivity();
            }
            
            // reset status vars
            Veri.Topic.topLinksLoaded = 0;
            Veri.Topic.pollSumsLoaded = 0;
            Veri.Topic.summListLoaded = 0;
            Veri.Topic.opinPrevLoaded = 0;
            Veri.Topic.shrTopicLoaded = 0;
            
            // figure out the bars separating the left hand items
            var arePollSummaries = ($('#' + Veri.Topic.domId('pollSummaries') + ' .pollSummary').length > 0);
            var areTopLinks      = ($('#' + Veri.Topic.domId('topLinks') + ' li').length > 0);
            var areSharedTopics  = ($('#' + Veri.Topic.domId('relatedTopics') + ' li').length > 0);

            if (areTopLinks) { $('#' + Veri.Topic.domId('topLinks')).show(); }
            else             { $('#' + Veri.Topic.domId('topLinks')).hide(); }

            if (areSharedTopics && areTopLinks) {
                $('#' + Veri.Topic.domId('linksAndTopicsSeparator')).show();
            } else {
                if (!arePollSummaries && !areSharedTopics && !areTopLinks) {
                    $('#' + Veri.Topic.domId('pollsAndLinksSeparator')).hide();
                }
                $('#' + Veri.Topic.domId('linksAndTopicsSeparator')).hide();
            }
        }
    }
}; // END Veri.Topic NAMESPACE


/**
 * 
 */
Veri.TopicOpinionPopup = {
    
    // State of topic/opinion popup:  either false (not showing), 'topic' or 'opinion'
    state   : false,

    // called after popup is closed
    onClose : null,

    setState : function(state)
    {
        if (!Veri.TopicOpinionPopup.state && state) {
            // catch browser forward/back navigation and hide popup
            var hashListener = HashListener.getInstance();

            if (!hashListener) {
                hashListener = HashListener.init(function() {}, {'dontRun' : true, 'fields' : {}});
            }

            hashListener.attachOnStateChange(function() {
                $('#viewOpinionPop').jqmHide();
            });
       
        }

        Veri.TopicOpinionPopup.state = state;
    },

    /*
     *  Close popup, keep track of in/out of popup
     */ 
    jqmClose : function(hash, callback) {
        jqmClose(hash, function() {
            if (typeof(callback) == 'function') {
                callback.apply(this, arguments);
            }
            if (typeof(Veri.TopicOpinionPopup.onClose) == 'function') {
                Veri.TopicOpinionPopup.onClose.apply(this, arguments);
                Veri.TopicOpinionPopup.onClose = null;
            }
        });

        Veri.TopicOpinionPopup.setState(false);
        HashListener.getInstance().detachOnStateChange();
        Veri.ViewTopic.resumeState();
    },

    /**
     *
     */
    goToCategory : function(el)
    {
        if (window.location.pathname == '/') { // we're on home page
            $('#viewOpinionPop').jqmHide();
            return HashListener.rebuildHash({'_ct': $(el).attr('veri_categoryId')});
        } else if (el.href.replace(new RegExp('^(https?://)?' + base_url), '') == window.location.pathname) { // we're on the category page
            $('#viewOpinionPop').jqmHide();
            return false;
        } else {
            return true; // let href fire
        }
    },

    /**
     * Bizzare IE behaviour prevents us from hiding the popup/topic panes.
     * Instead we empty the pane and save the html in a variable
     */
    ieSave : { 'topic' : null, 'opinion' : null },

    correctIeBug: function(toState)
    {
        if (!$.browser.msie || (Veri.TopicOpinionPopup.state == toState)) { return; }
        var fromState = (toState == 'topic') ? 'opinion' : 'topic';

        if (!Veri.TopicOpinionPopup.ieSave[fromState]) {
            Veri.TopicOpinionPopup.ieSave[fromState]
                        = $('#pop' + fromState.ucFirst() + 'Wrap').html();
            $('#pop' + fromState.ucFirst() + 'Wrap').empty();
        }


        if (Veri.TopicOpinionPopup.ieSave[toState]) {
            $('#pop' + toState.ucFirst() + 'Wrap').html(
                                Veri.TopicOpinionPopup.ieSave[toState]);
            Veri.TopicOpinionPopup.ieSave[toState] = null;
        }
    }
};

/***
 **
 ** begin: ARTICLE FUNCTIONALITY
 **
 ***/
if (Veri.Article == null || typeof(Veri.Article) != "object") { Veri.Article = new Object(); }

// Begin Definition
Veri.Article = {

    inPopup : function(){
       return Veri.TopicOpinionPopup.state == 'opinion';
    },

    /**
     * View Article
     *
     * This function will be overwritten if the browser supports the opinion popup
     **/
    view : function()
    {
        return true;
    },

    /**
     * popReportOpinion()  
     *
     * Launches the "Report Abuse" popup.  
     *
     * @param int $opinionId Opinion ID
     * @param int $topicId   Topic ID
     * @returns void
     */
    popReportOpinion : function(opinionId,topicId) 
    {
        if (!USER.isLoggedIn)
        {
            Veri.Alert.triggerAlert('error',"Only registered users may report an article. Please login or signup.");
        }else{
            // Hide the article popup (we dont want to see it anymore if reporting abuse)
            $('#viewOpinionPop').jqmHide();
            // Set the opinion/topic id and launch the popup
            $("#reportAbuseOpinionId").val(opinionId);
            $("#reportAbuseTopicId").val(topicId);
            $("#reportAbuseReason")[0].selectedIndex = 0;
            $("#reportAbuseMessage").val('');
            $("#reportAbusePop").jqmShow();
        }
    },

    /**
     * submitModerationReport()
     *
     * Launches an ajax request to report the opinion
     *
     * @returns void
     */
    submitModerationReport : function()
    {
        if (!USER.isLoggedIn)
        {
            Veri.Alert.triggerAlert('error',"Only registered users may report an article. Please login or signup.");
        }else if (!$("#reportAbuseReason").val()){

            Veri.Alert.triggerAlert('error',"Please select a reason!");

        }else{

            // Close the popup window
            $("#reportAbusePop").jqmHide();
            // Grab the details we need for the report
            var reportData = 'opinion='+$("#reportAbuseOpinionId").val()
                        +'&topic='+$("#reportAbuseTopicId").val()
                        +'&reason='+encodeURIComponent($("#reportAbuseReason").val())
                        +'&message='+encodeURIComponent($("#reportAbuseMessage").val());

            // Submit the new report
            $.ajax({type: "POST",
                    url:  base_url + '/OpinionService/reportOpinion/',
                    data: reportData,
                    dataType: 'xml',
                    complete: function(xml,status)
                    {
                        if (status == 'success') {
                            if ($('result', xml.responseXML).text() > 0) {
                                Veri.Alert.triggerAlert('success',
                                            "Thank you, your report has been submitted!");
                            } else {
                                Veri.Alert.triggerAlert('error',
                                            "You have already reported this article!");
                            }
                        }
                    }
             });
        }
    }

};
// End Veri.Article

/**
 *
 */
Veri.ViewTopic = 
{
    view: function() { return true; } // topic loads on new page by default
}; // End Veri.ViewTopic 

/*
 * if topic/opinion popup is supported
 */
if ((typeof(gIsIphone) == 'undefined') || !gIsIphone) {
    // add functionality for popup

    /*
     * extend Article
     */
    $.extend(Veri.Article,
    {
        currentParams    : null,
        currentTopicHash : null,
        currentHash      : null,
        currentViewBy    : null,
        prevHash         : null,
        nextHash         : null,

        /**
         * View Article In Lightbox
         *
         * topic_hash - hash for topic OR null if it's next/previous navigation
         * opinion_hash - hash for opinion
         * params - param string ("period=all&whatever=good")
         *
         **/
        view : function(topic_hash,opinion_hash,params, html)
        {
            Veri.TopicOpinionPopup.correctIeBug('opinion');

            // figure out the sort by param
            if ((typeof(topicId) != 'undefined') && (topicId > 0)) { // we are in topic page
                var viewBy = $('#Rank').val();
            } else {
                var viewBy = null;
            }

            if (   (opinion_hash != Veri.Article.currentHash)
                || (topic_hash   != Veri.Article.currentTopicHash)
                || (viewBy       != Veri.Article.currentViewBy)
                || (params       != Veri.Article.currentParams)) { // state has changed

                Veri.Article.currentHash   = opinion_hash;
                Veri.Article.currentViewBy = viewBy;

                // Clear Old Stuff:
                // Clear content
                $("#popOpinionContent").html('');

                // Clear next previous stuff
                $(['next', 'prev']).each(function() {
                    Veri.Article[this + 'Hash'] = null; 
                    $('#popOpinionNav' + this.ucFirst()).removeAttr('href')
                                                        .addClass('disabled')
                                                        .addClass('notALink');
                });

                // Clear topic link in navbar
                if (topic_hash === null) { // next/previous navigation, use same topic
                    topic_hash = Veri.Article.currentTopicHash;
                } else { // wait for the metadata to comeback before enabling the link
                    Veri.Article.currentTopicHash = topic_hash;
                    $('#popOpinionNavTopicLink').attr('innerHTML', '')
                                                .removeAttr('href');
                    // Hide Topic and Next/Prev
                    $('#popOpinionNavTopic').hide();
                    $('#popOpinionNavNextPrev').hide();
                }
         
                // DONE clearing stuff


                // Load Opinion
                // Param set?
                if (typeof(params) == 'string'){
                    var param_string = '&' + params;
                    Veri.Article.currentParams = params;
                }
                else {
                    var param_string = '';
                    Veri.Article.currentParams = null;
                }

                // figure out the sort by param
                if (viewBy) {
                    param_string += '&view=' + $('#Rank').val();
                }

                function handleLoad()
                {
                     // add next and previous links in nav bar
                     $(['next', 'prev']).each(function() {
                         var uc = this.ucFirst();
                         var url = ($('#opinionMetadata' + uc + 'Url').val());
                         if (url.length > 0) {
                             Veri.Article[this + 'Hash'] = url.match(/(\w+)\/$/)[1];
                             $('#popOpinionNav' + uc).attr('href', url)
                                                     .removeClass('disabled')
                                                     .removeClass('notALink');
                         } else {
                             Veri.Article[this + 'Hash'] = null;
                         }
                     });

                     // set up the topic link in nav bar
                     $('#popOpinionNavTopicLink').attr(
                         {'innerHTML': elipse($('#opinionMetadataTopicTitle').val(), 85),
                          'alt' : $('#opinionMetadataTopicTitle').val(),
                          'href'     : $('#opinionMetadataTopicUrl').val()}
                     );

                     // Show Topic and Next/Prev
                     $('#popOpinionNavTopic').fadeIn(js_fade_speed);
                     $('#popOpinionNavNextPrev').fadeIn(js_fade_speed);

                     Veri.Vote.enableInteractivity();
                     
                     $('#popOpinionContent').scrollTop(0);

                }

                if (typeof(html) == 'string') {
                    $("#popOpinionContent").html(html);
                    handleLoad();
                } else {

                    // fire
                    $("#popOpinionContent").fillFromAjax(
                          base_url + '/Topic/opinionView/?opinion=' + opinion_hash
                                                       + '&topic=' + topic_hash
                                                       + '&renderFullPage=0'
                                                       + param_string,
                          handleLoad,
                          $.SHOW_LOADING_MESSAGE);

                    // Track This Article View With Google
                    //pageTracker._trackEvent('Article View', 'Popup', '/'+topic_hash+"/"+opinion_hash+"/");
                    pageTracker._trackPageview('/ajax/'+topic_hash+"/"+opinion_hash+"/");
                }
            }  // end if state has changed (not same opinion, we have to load)

            // show pane/popup
            switch (Veri.TopicOpinionPopup.state) {
                case 'opinion':
                    break;
                case 'topic':
                    fadeOutAndIn('#popTopicWrap', '#popOpinionWrap');
                    break;
                default:
                    $('#popTopicWrap').hide();
                    $('#popOpinionWrap').show();
                    $('#viewOpinionPop').jqmShow();
            }

            Veri.TopicOpinionPopup.setState('opinion');

            if(window.event) window.event.cancelBubble = true;

            return false; 
        },

        /**
         *
         */
        navigate : function(prevOrNext)
        {
            if (Veri.Article[prevOrNext + 'Hash']) {
                Veri.Article.view(null,
                                  Veri.Article[prevOrNext + 'Hash'],
                                  Veri.Article.currentParams);
            }

            return false;
        },

        /**
         *
         */
        goToTopic: function()
        {
            if (Veri.Article.currentTopicHash
                == document.location.pathname.match(/(\w*)\/?$/)[1]) {
                // just close the popup if we're already on the topic page
                $('#viewOpinionPop').jqmHide();
                scrollToTopSmooth();
                return false;
            }
        }
    });

    /*
     * Extend ViewTopic
     */
    $.extend(Veri.ViewTopic, {

        onHide : function() {
            Veri.Topic.inPopup = 0;
        },

        stateDefaults: {
            '_pg' : 1,
            '_ur' : 0,
            '_vw' : 'top',
            '_pd' : '', // will be different for different topics
            '_pl' : 0,
            '_o1' : 0,
            '_o2' : 0,
            '_qr' : ''
        },

        state: {
            'url'    : null,
            'params' : {}
        },

        topicStateInitialized: false,

        // where we keep the topic id for the topic page while the popup is showing
        topicIdSave : null,

        /**
         * sets up state variables and callback for topic popup
         */
        initState: function(topicId)
        {
            if (!Veri.ViewTopic.topicStateInitialized) {

                Veri.ViewTopic.topicIdSave = Veri.Topic.topicId;
            }

            // tell Topic static class which topic id we're dealing with
            Veri.Topic.topicId = topicId;

            if (!Veri.ViewTopic.topicStateInitialized) {

                Veri.ViewTopic.topicStateInitialized = true;

                Veri.Topic.inPopup = 1;

                // catch intra-page navigations: pass params straight into
                // Veri.Topic.loadTopic without changing location hash
                HashListener.getInstance().attachOnNav(function(newOpts, reset) {

                    if (!reset) {
                        newOpts = arrayMerge(Veri.ViewTopic.state.params, newOpts);
                    }

                    var validated = HashListener.validateChangedFields(Veri.ViewTopic.stateDefaults,
                                                                       false,
                                                                       newOpts,
                                                                       Veri.ViewTopic.state.params);

                    $('#popTopicContent input[hashOpt]').each(function() {
                        $(this).val(validated.opts[$(this).attr('hashOpt')]);
                    });
                    $('#popTopicContent select[hashOpt]').each(function() {
                        $(this).selectByValue(validated.opts[$(this).attr('hashOpt')]);
                    });

                    Veri.ViewTopic.state.params = validated.opts;

                    if ((function(test) { // do nothing if opts hasnt changed
                        for (var i in test) { if (test[i]) { return false; } }
                        return true;
                    })(validated.optsChanged)) { return false; }

                    // refresh
                    Veri.Topic.loadTopic(validated.opts,
                                         false,
                                         false,
                                         validated.isDefault,
                                         validated.optsChanged);
                    return false;
                });
            }
        },

        /**
         * resets state variables and callbacks for topic popup (on hide popup)
         */
        resumeState: function()
        {
            if (Veri.ViewTopic.topicStateInitialized) {
                Veri.ViewTopic.topicStateInitialized = false;
                Veri.Topic.topicId = Veri.ViewTopic.topicIdSave;
                Veri.Topic.inPopup = 0;
                HashListener.getInstance().detachOnNav();
            }
        },

        /**
         * view topic in popup
         */
        view: function(el, onClose, showShare)
        {

            if (typeof(el) == 'string') {
                var url = el;
                el = null;
            } else {
                var url = el.href;
            } 

            Veri.TopicOpinionPopup.correctIeBug('topic');

            if (typeof(onClose) == 'function') { Veri.TopicOpinionPopup.onClose = onClose; }

            var urlNoParams = url.split('?')[0];

            if (urlNoParams == Veri.ViewTopic.state.url) {
                // same topic, go to page one in popup.  Will be caught, location will not change.
                HashListener.rebuildHash({'_pg' : 1}, true);
            } else {
                Veri.ViewTopic.state.url = urlNoParams;
                Veri.ViewTopic.state.params = {};
                url = url + ((url.indexOf('?' == -1)) ? '?' : '&') + 'renderFullPage=0';

                if (!Veri.TopicOpinionPopup.state) { // not from popup
                    $('#popTopicWrap').empty();
                }

                // load content
                $('#popTopicWrap').fillFromAjax(
                    url,
                    function(response) {
                        // setup state logic
                        Veri.ViewTopic.initState($('#topicMetadata_id').val());
                        Veri.ViewTopic.stateDefaults['_pd'] = ($('#topicMetadata_defaultPeriod').val());
                        Veri.Vote.enableInteractivity();
                        Veri.Topic.enablePollSummaryInteractivity();
                        // show share layer if requested
                        if((typeof showShare != "undefined") && showShare){
                            Veri.Topic.showShare();
                        }
                    },
                    $.SHOW_LOADING_MESSAGE
                );

                // Track This Topic View With Google
                //pageTracker._trackEvent('Topic View', 'Popup', "/"+url.split('/')[4]+"/");
                pageTracker._trackPageview("/ajax/"+url.split('/')[4]+"/");
            }

            // show pane/popup
            switch (Veri.TopicOpinionPopup.state) {
                case 'topic':
                    break;
                case 'opinion':
                    fadeOutAndIn('#popOpinionWrap', '#popTopicWrap');
                    break;
                default:
                    $('#popOpinionWrap').hide();
                    $('#popTopicWrap').show();
                    $('#viewOpinionPop').jqmShow();
            }
            Veri.TopicOpinionPopup.setState('topic');

            if(window.event) window.event.cancelBubble = true;

            return false;
        }
    });

} else { // it is iphone: don't do popups for topic/opinion

    $.extend(Veri.Article,
    {
        view: function(topic_hash, opinion_hash, params, el) {
            var $el = $(el)
            if ($el.attr('href').indexOf('void(0)') > -1) {
                // no href: assume it's a help link
                $el.attr('href', base_url + '/'
                               + 'Veritocracy/'
                               + topic_hash + '/'
                               + opinion_hash + '/');
            }

            $el.attr('target', '_blank');
            return true;
        }
    });
}


/***
 **
 ** begin: GLOBAL FUNCTIONALITY
 **
 ***/

function log(value) {
    if (typeof(console) != 'object') { // ie
        alert(value);
        return;
    }

    if (typeof(console.debug) == 'function') { // firefox
        console.debug(value);
        return;
    }

    if (typeof(console.log) == 'function') { // safari
        console.log(value);
        return;
    }
}

function logError(e) {
    if (e instanceof Error) {
        log(e.name + ' in '
          + e.fileName + ' on line '
          + e.lineNumber + ': '
          + e.message);
    } else {
        log(e);
    }
}

/**
 * arrayKeys() - equivalent to php array_keys()
 */
function arrayKeys(obj)
{
    var retArr = [];
    for (var i in obj) { retArr.push(i); }
    return retArr;
}

/**
 * arrayKeys() - equivalent to php array_merge()
 */
function arrayMerge(/* objects */)
{
    var retObj = {};

    for (var i = 0; i < arguments.length; i++) {

        if (typeof(arguments[i]) != 'object') { continue; }

        for (var ii in arguments[i]) {
            retObj[ii] = arguments[i][ii];
        }
    }

    return retObj;
}

$.extend(String.prototype, {

    ucFirst: function()
    {
        return this.substr(0,1).toUpperCase() + this.substr(1);
    },

    htmlEntities: function()
    {
        return this.replace('&', '&amp;')
                   .replace('>', '&gt;')
                   .replace('<', '&lt;') 
                   .replace("'", '&apos;')
                   .replace('"', '&quot;');
    },

    strstr: function(needle)
    {
        var index = this.indexOf(needle);
        return (index == -1) ? null : this.substr(index);
    },

    strrstr: function(needle)
    {
        var index = this.lastIndexOf(needle);
        return (index == -1) ? null : this.substr(index);
    }

/*
    urlToParamsArray: function()
    {
        var retArr = {};

        var paramsStr = this.strstr('?');

        if (paramsStr) {
            var splitParams = paramsStr.split('&');
            for (var param = splitParams.unshift(); splitParams.length; param = splitParams.unshift()) {
                var parts = param.split('=', 2);
                retArr[parts[0]] = (parts.length == 2)
                                 ? parts[1]
                                 : '';
            }
        }

        return retArr;
    }
    */
});

Date.monthNames = ['January',
                   'Feburary',
                   'March',
                   'April',
                   'May',
                   'June',
                   'July',
                   'August',
                   'September',
                   'October',
                   'November',
                   'December'];

Date.prototype.humanReadable = function()
{
    return Date.monthNames[this.getMonth()] + ' '
         + this.getDate() + ', ' + this.getFullYear();
}

/*
 *  Update Unread Message Count In Nav "My Account" button
 */
function updateUnreadMessageCount(count){
    var text = '';
    // Make sure we have an integer
    count = parseInt(count);
    if (count > 100){
        text = ' (100+)';
    } else if (count > 0 && count < 100){
        text = ' ('+count+')';
    }
    $("#nav_unreadMessageCount").html(text);
}

/*
 * static class AjaxHandler
 *
 * Global Handler For Ajax Exceptions / Errors
 */ 
var AjaxHandler = {

    /**
     * handleError()
     *
     * Global ajax error handling function.
     *
     * @param XmlHttpRequest req
     * @param string         errorType - 'error', 'parseerror' or 'timeout'
     */
    handleError: function(req, errorType, e)
    {
        // autocomplete request are abortable and handled by the jquery ajaxQueue plugin
        if (this.mode == 'abort') { return; }

        if (errorType != 'timeout') {
            // if the error is the result of an aborted ajax call we should ignore it.
            var ignoreError = true;
            try {
                // sometimes if a request is aborted, trying to access status property
                // throws an error
                ignoreError = (req.status == 0 || req.status == '');
            } catch (e) {}

            if (ignoreError) {
                this.complete = false;
                return;
            }
        }

        var errorMessage = (errorType == 'timeout')
                         ? 'Unable to contact the server, your internet connection '
                           + 'may be down, please try again later.'
                         : "There was an error communicating with the server.";

        // User Error
        Veri.Alert.triggerAlert("error", errorMessage);

        // Developer Message
        if (DEBUG_MODE && (errorType != 'timeout')) {
            alert(errorType + ": " + req.responseText);
        }
    },

    /**
     * handleSuccess()
     *
     * Global ajax success callback.  Handles user not logged in error resonse.
     *
     * @param mixed response - response data (string) or an XML dom document
     *                         object (processed by jquery).
     */
    handleSuccess: function(response)
    {
        if (typeof(ActiveXObject) == 'undefined') { // decent browsers
            if (typeof(response.xmlVersion) == 'undefined') {
                // we're not dealing with an xml response, continue as noremal
                return;
            }
        } else { // explorer
            if (!(response instanceof ActiveXObject)) {
                return;
            }
        }

        // it's an xml document.

        if ($('result', response).text() == AjaxHandler.userNotLoggedInCode) {
            // server returned a user not logged in error'

            Veri.Alert.triggerAlert('warn',
                "You are not logged in, you may have logged out in another window.");

            if (!UserCookieListener.testCookie()) {
                // the USERLOGGEDIN cookie says we're logged in but the ajax response
                // says otherwise we're not.  This shouldn't happen, but if it does
                // we need to correct the discrepancy by deleting the cookie
                document.cookie = 'USERLOGGEDIN=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/';

                USER.isLoggedIn = 0;
                hideAccountLinks();

                // call refresh hanlder
                _log_in_out_data_refesh(true /* logged out in other window */);
            }

            // don't fire the specific ajax callback
            this.complete = null;

            if (typeof(this.handleNotLoggedIn) == 'function') {
                this.handleNotLoggedIn(response);
            }

        } else if ($('result', response).text() == AjaxHandler.invalidInputCode) {
            // handle invalid input
            var complete = this.complete;
            this.complete = null;

            Veri.Alert.hideAlert();
            Veri.Alert.triggerAlert('error', 'Invalid Input');

            complete.call(this, null, 'error');
        }
    }
} // END NAMESPACE - AjaxHandler

function preloadImages(images)
{
    for (var i = 0; i < images.length; i++) {
        (new Image()).src = image_base + images[i];
    }
}

/*
 * jqmOpen/Close: These are the methods that handle displaying and 
 *                and hiding the dhtml popup windows. We use these
 *                to shrink and grow the window into view. In order
 *                to prevent the content inside of the windows from
 *                jumbling around as the window shrinks/grows we clone
 *                the popup and shrink / grow the blank clone then 
 *                fade in / out the actual content at the last moment. 
 */

/*
 * callbacks: callbacks that are called after the popup window has finished 
 *            showing/hide can either be set globally by defining a function
 *            that calls jqmOpen passing the callback second argument at the
 *            beginning of the script eg.
 *            $(el).jqm({'onShow' : function(h) { jqmOpen(h, callback); }});
 *            or a callback can be passed in to $.fn.jqmShow/jqmHide when
 *            showing/hiding eg. $(el).jqmShow(callback);
 *            We extended jqmShow and jqmHide functions in order to accept a
 *            callback argument that gets stored in the variable jqmCallbacks.
 */
var jqmCallbacks = {};

var jqmOpen = function(hash, callback) {
    
    callback        = (typeof(callback) == 'function') ? callback : function(){};

    if ((typeof(jqmCallbacks[hash.w.attr('id')]) != 'undefined') &&
        (typeof(jqmCallbacks[hash.w.attr('id')].jqmShow) != 'undefined')) {

        dynamicCallback = jqmCallbacks[hash.w.attr('id')].jqmShow;
        delete jqmCallbacks[hash.w.attr('id')].jqmShow;
    } else {
        dynamicCallback = function(){};
    }
    // Set the Position (of both real and placeholder)
    // Top = 75px From Top of Active Window Area (set in css)
    // Left = Centered To Screen Window
    hash.w.css("left",($(window).width()-hash.w.width())/2);

    // Show Overlay
    hash.o.show();

    // Show Window
    hash.w.show();

    // jqm focuses the first input field in the popup on show.
    // we don't want this because it clears our hint text.
    // to do this we blur the first input field as soon as it's focused by jqm
    // but in safari it gets focused twice.
    var safariFocusTwice = $.browser.safari;

    function blurFirstInput()
    {
        this.blur();
        if (!safariFocusTwice) {
            $(this).unbind('focus', blurFirstInput);
        } else { // first safari focus
            safariFocusTwice = false;
        }
    }

    $(':input:visible:eq(0)', hash.w).bind('focus', blurFirstInput);

    dynamicCallback();
    callback();
};

var jqmOpenKick = function(hash, callback)
{
    hash.o.unbind('click');
    jqmOpen(hash, callback);
};

var jqmClose = function(hash, callback) {

    callback        = (typeof(callback) == 'function') ? callback : function(){};
    if ((typeof(jqmCallbacks[hash.w.attr('id')]) != 'undefined') &&
        (typeof(jqmCallbacks[hash.w.attr('id')].jqmHide) != 'undefined')) {

        var dynamicCallback = jqmCallbacks[hash.w.attr('id')].jqmHide;
        delete jqmCallbacks[hash.w.attr('id')].jqmHide;
    } else {
        var dynamicCallback = function(){};
    }

    // IE has weird issue with transparency and	fadeIn/Out
    if ($.browser.msie){
        hash.w.hide();
        dynamicCallback();
        callback();
    } else {
        // Hide the real popup
        hash.w.fadeOut('normal', function() { dynamicCallback(); callback(); });
    } 

    // Hide Overlay
    hash.o.hide();
};

var jqmCloseKick = function(hash, callback)
{
    Kickstart.closingStarted = true;

    if(Kickstart.needsSaving)
       Kickstart.submit();
    
    jqmClose(hash, callback);

    Kickstart.closingStarted = false;
};

/**
 * showLoading() - Show a translucent layer above an element extending over the
 *                 full width and height of the element.
 *
 * NOTE: Applying this function to a non-positioned element will yield VERY INCONSISTENT
 * results. (particulailry in IE)
 *
 * @param mixed    domIdOrEl - id of element, or element we're showing the layer over
 * @param function callback  - called after the layer is faded in.
 */
function showLoading(domIdOrEl, callback)
{
    // first argument can be either DOM id or dom element
    if (typeof(domIdOrEl) == 'string') { // DOM id 
        var $el = $('#' + domIdOrEl);
        var domId = domIdOrEl;
    } else {                             // element
        var $el = $(domIdOrEl);
        var domId = $el.attr('id');
    }
	
    if ($el.attr('loadingEffect')) { return;} // already trying to show
    $el.attr('loadingEffect', '1');

    var $loadingLayer = $(
          '<div class="loadingLayer" id="' + domId + '_loading' + '" style="display:none">'
        + ((($el.height() < 250) && ($el.height() != 0)) ? '  <table><tbody><tr><td><div>'
                          : '  <div style="top:100px;">')
        + '    <img src="' + image_base + 'loading.v0.gif" />'
        + '    <div>Loading.  Please Wait...</div>'
        + ((($el.height() < 250) && ($el.height() != 0)) ? '  </div></td></tr></tbody></table>'
                          : '  </div>')
    );

    if ($el.css('position') != 'static') {
        // we shouldn't be applying the layer to non positioned elements anyway
        $loadingLayer.css({'top'  : '0px',
                           'left' : '0px'});
    }
    
    // set the opacity for hidden elements.
    $el.children(':hidden').addClass('fadedLoading').opacity(0.15);

    // fade out the visible ones.
    var $elementsToFadeOut = $el.children(':visible').addClass('fadedLoading');
    var totalElements = $elementsToFadeOut.length;

    $el.prepend($loadingLayer);

    if (totalElements < 1) {
        $loadingLayer.fadeIn(js_fade_speed, function() {
            if (typeof(callback) == 'function') { callback(); }
        });
    } else {
        // fade the content
        $elementsToFadeOut.fadeTo(js_fade_speed, 0.15, function() {
            if (--totalElements == 0) {
                if (!$el.attr('loadingEffect')) {return;} // hideLoading() already called
                $el.prepend($loadingLayer);
                $loadingLayer.fadeIn(js_fade_speed, function() {
                    if (typeof(callback) == 'function') { callback(); }
                });
            }
        });
    }

   is_loading = true;

}

/**
 * hideLoading() - Hide translucent layer above an element.
 *
 * @param mixed    domIdOrEl - id of element, or element we're hiding the layer for
 * @param function callback  - called after the layer is faded out.
 */
function hideLoading(domIdOrEl, callback)
{ 
    // first argument can be either DOM id or dom element
    if (typeof(domIdOrEl) == 'string') { // DOM id 
        var $el = $('#' + domIdOrEl);
        var domId = domIdOrEl;
    } else {                             // element
        var $el = $(domIdOrEl);
        var domId = $el.attr('id');
    }
    
    $el.removeAttr('loadingEffect');

    var $loadingLayer = $('#' + domId + '_loading');

    $loadingLayer.fadeOut(js_fade_speed, function() {
        $loadingLayer.remove();

        $('.fadedLoading:hidden', $el).opacity(1);

        var $elementsToFadeIn = $('.fadedLoading:visible', $el)
        var totalElements = $elementsToFadeIn.length;

        $elementsToFadeIn.fadeTo(js_fade_speed, 1, function() {
            if (--totalElements == 0) {
                if ($.browser.msie) {
                    $elementsToFadeIn.each(function() {
                        this.style.removeAttribute('filter');
                    });
                }
                if (typeof(callback) == 'function') { callback(); }
            }
        });

        $('.fadedLoading', $el).removeClass('fadedLoading');
    });

    is_loading = false;
}

/*
 *  Check if the user can post an opinion and handle
 *  accordingly.
 */
function canPostOpinion(query_string){

    if (USER.isLoggedIn && USER.confirmed){
        // Can post
        goToPostOpinion(query_string);
    } else {   
        // Can't post; Handle Rejection
        handleRejectedFromPost(query_string);
    }

    return false;
}

function goToPostOpinion(query_string)
{
    var hash = window.location.hash.replace(/^#/, '');
    hash = (hash != '') ? 'refererHash=' + escape(hash) : '';

    if (query_string && query_string != '') {
        query_string += (hash != '') ? ('&' + hash) : '';
    } else {
        query_string = (hash != '') ? (hash) : '';
    }

    setTimeout('window.location = base_url + "/post/?' + query_string + '"',1000);

    return false;
}

/*
 *  Check if the user can access settings and handle
 *  accordingly.
 */
function canAccessSettings()
{
    // Can post
    if (USER.isLoggedIn) {
        return true;
    }
    // Cant post
    else {
        // Handle Rejection
        handleRejectedFromSettings();
        return false;
    }

}

/*
 *  Called if a user is rejected from posting an opinion
 *  (because the user is not logged in or not confirmed)
 */
function handleRejectedFromPost(query_string){

    // User is not logged in
    if (!USER.isLoggedIn) {
        Veri.Alert.triggerAlert("warn", "You must be logged in with a confirmed email address to post opinions. Please login below.");
        // Function to call once user logs in or signs up
        var rejectedFromPostCallback = function() {
            if (USER.confirmed) {
                query_string = (typeof(query_string) == 'string' && query_string != '')
                             ? '?' + query_string : '';
                // Give the user 1 second to see success messages, then redirect to post opinion
                setTimeout('goToPostOpinion("' + query_string + '");', 1000);
                return false; // don't refresh the page.
            } else {
                Veri.Alert.triggerAlert("warn","Please confirm your email address now to submit an article.");
            }
        }
        // Have the user login or signup
        lsp_show(rejectedFromPostCallback);
    }
    // User is logged in, but is not confirmed
    else {
        Veri.Alert.triggerAlert("warn","Please confirm your email address.");
    }

}

/*
 *  Called if a user is rejected from accessing settings
 *  (because the user is not logged in or not confirmed)
 */
function handleRejectedFromSettings() {

    // User is not logged in
    if (!USER.isLoggedIn) {
        Veri.Alert.triggerAlert("warn", "You must be logged in to access settings. Please login below.");
        // Function to call once user logs in or signs up
        var rejectedFromSettingsCallback = function() {
            // Give the user 1 second to see success messages, then redirect to post opinion
            setTimeout('window.location = base_url + "/prefs/"',1000);
            return false; // don't refresh the page.
        }
        // Have the user login or signup
        lsp_show(rejectedFromSettingsCallback);
    }
}

/*
 *  Visually and functionally disable a styled form button
 */
function disableStyledButton(buttonId){

    $("#"+buttonId).attr('disabled','disabled');
    $("#"+buttonId).addClass('disabled');

}

/*
 *  Visually and functionally enable a styled form button
 */
function enableStyledButton(buttonId){

    $("#"+buttonId).removeAttr('disabled');
    $("#"+buttonId).removeClass('disabled');

}


/*
 *  Scrolls the user to the top of the page via fade/in out
 *  Used for major page load changes (change pages, etc...)
 *  (basically anytime the page should be scrolled to top
 *   because of new data being loaded)
 */
function scrollToTopSmooth(){

    // Dont Go To Top If Already Near Top
    // (300 = beginning of content area)
    if($(window).scrollTop()<300){
        return;
    }

    // Instead of fading out the whole page which requires 
    // serious cpu, we fade in a white cover div
    $("#scrollToTopSmoothCover").fadeIn("normal",function(){
         scrollTo(0,0);
         $("#scrollToTopSmoothCover").fadeOut("normal");
    });

}

function cup_submitContact(){

        // User Must enter a contact message
        if ($("#cup_contactMessage").val() == '' || $("#cup_contactMessage").val().length<1){
             Veri.Alert.triggerAlert('error', "Please enter a message");
             return 0;
        }

        Veri.Alert.triggerAlert('loading', "Sending your message, please hold...");

        // Compose Data
        var contactData = '1=1';
        if(USER.isLoggedIn == 1){
            contactData += '&username=' + USER.username; 
        }
        if($("#cup_contactEmail").val() != '' || $("#cup_contactMessage").val().length>0){
            contactData += '&email=' + $("#cup_contactEmail").val();
        }
        contactData += '&subject=' + $("#cup_contactSubject").val();
        contactData += '&message=' + $("#cup_contactMessage").val();

        // Submit the new report
        $.ajax({type: "POST",
                url:  base_url + '/ContactService/contact/',
                data: contactData,
                dataType: 'xml',
                complete: function(xml,status)
                {
                    Veri.Alert.hideAlert();
                    if (status == 'success') {
                        if ($('result', xml.responseXML).text() > 0) {
                            Veri.Alert.triggerAlert('success',
                                        "Thank you, your message has been sent!");
                            $('#contactPop').jqmHide();
                            // Clear Popup Form
                            $("#cup_contactMessage").val('');
                            $("#cup_contactSubject")[0].selectedIndex = 0;
                            $("#cup_contactEmail").val('');
                        } else {
                            Veri.Alert.triggerAlert('error',
                                        "There was an error sending your message. Please contact support@veri.com");
                        }
                    }
                }
         });

}

/* 
 *  Show the what is veri popup
 */
function whatIsVeri(slide){
    if (typeof slide != "undefined" && slide != ''){
        wiv_gotoSlide(slide);
    }
    $("#whatIsVeriPop").jqmShow();
}

/*
 *  What Is Veri: Go To Slide 
 */
function wiv_gotoSlide(slide){

    // Change the nav
    $("#whatIsVeriNav").find("div").each(function() {
        $(this).removeClass("selected");
    });    
    $("#whatIsVeri"+slide).addClass("selected");

    // Change teh frame
    $("#whatIsVeriPop").find(".frame").each(function() {
        // Hide the old frame
        if ($(this).is(":visible")){
            $(this).fadeOut('fast', function(){
                // Show the new one
                if (!$("#whatIsVeriFrame"+slide).is(":visible")){
                    $("#whatIsVeriFrame"+slide).fadeIn('fast');
                }
            });
        }
    });
}

/**
 * JQuery extensions.
 *
 * INTERFACE
 *
 * @method setHint() - sets the hint text for an input.
 *     @param string - the hint text.
 *
 * @method fillFromAjax() - Load ajax xhtml data and set the element's innerHTML to the response.
 *                          Similar to load() except that this function does not wipe out the
 *                          content of the target until after it's loaded the data.
 *
 *     @param string   url             - ajax url
 *     @param function callback        - called once the data is recieved
 *     @param bool     useLoadingLayer - if set to true, show a translucent layer over the
 *                                       element while the data is loading.
 *                                       NOTE: use only for positioned elements with this arg.
 *
 * @method selectByValue() - Finds an <option> by value and selects it.
 */
(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;

    // constants for fading for ajax loading
    $.LOAD_FADED           = 01;
    $.LOADING_MESSAGE_FLAG = 02;
    $.SHOW_LOADING_MESSAGE = $.LOAD_FADED | $.LOADING_MESSAGE_FLAG;

    $.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, '');
        },

        /**
         * handleHintBlur() - callback for blur of input field
         */
        handleHintBlur: function()
        {
            if (parentValFunc.call(this) === '') {
                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;
            }
        },

        opacity: function(opacity)
        {
            if ($.browser.msie && (opacity == 1)) {
                this.each(function() { this.style.removeAttribute('filter'); });
            } 

            this.css((($.browser.msie)
                      ? {'filter' : 'alpha(opacity=' + (opacity * 100) + ')'}
                      : {'opacity' : opacity })); 

            return this;
        },

        addFaded: function(html)
        {
            var $tmpDiv = $('<div></div>');
            $tmpDiv.attr('innerHTML', html);

            $tmpDiv.children().addClass('fadedLoading')
                              .opacity(0.15);

            this.attr('innerHTML', $tmpDiv.attr('innerHTML') + this.attr('innerHTML'));

            return this;
        },

        /**
         * fillFromAjax() - Load ajax xhtml data and set the element's innerHTML to the response.
         *                  Similar to load() except that this function does not wipe out the
         *                  content of the target until after it's loaded the data.
         *
         * @param string   url      - ajax url.
         * @param function callback - called once the data is recieved;
         * @param int      fadeMode - if set to SHOW_LOADING_MESSAGE show a translucent
         *                            layer over the element while the data is loading.
         *                            if set to LOAD_FADED, load all data faded out.
         */
        fillFromAjax: function(url, callback, fadeMode) {

            fadeMode = (typeof(fadeMode) == 'undefined') ? 00 : fadeMode;

            if (fadeMode & $.LOADING_MESSAGE_FLAG) {
                showLoading(this);
            }

            var me = this;
            $.ajax({'url'      : url,
                    'type'     : 'GET',
                    'dataType' : 'xhtml',
                    'complete' : function(req, status)
                        {
                           if (status == 'success') {
                                // remove the old stuff
                                me.children().not('#' + me.attr('id') + '_loading')
                                                                           .remove();
                                if (fadeMode & $.LOAD_FADED) {
                                    me.addFaded(req.responseText);
                                } else {
                                    me.attr('innerHTML', req.responseText);
                                }
                            }

                            if (fadeMode & $.LOADING_MESSAGE_FLAG) { 
                                hideLoading(me);
                            }

                            if (typeof(callback) == 'function') {
                                callback.call(me, req.responseText, status, req);
                            }
                        }
                   }
            );
            return this;
        },

        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);
        }

    });

    // make jqm functions accept a callback: here we set a callback to a global
    // variable (jqmCallbacks).  In our jqmOpen() and jqmClose() functions we call
    // the callbacks.  Callbacks are passed as the first argument to the functions.
    if ($.fn.jqm) {

        $(['jqmShow', 'jqmHide']).each(function() {
            var me = this;
            var parentFunc = $.fn[this];
            $.fn[this] = function(callback) {
                if (typeof(callback) == 'function') {
                    if (!jqmCallbacks[this.attr('id')]) {
                        jqmCallbacks[this.attr('id')] = {};
                    }
                    jqmCallbacks[this.attr('id')][me] = callback;
                }
                return parentFunc.apply(this, arguments);
            }
        });
    }

})(jQuery);

function fadeOutAndIn(elOut, elIn)
{
    var doneFade = false;

    if ($(elOut).is(':visible')) {
        $(elOut).fadeOut(js_fade_speed, function() {
            if (!doneFade) {
                if (!$(elIn).is(':visible')) {
                    $(elIn).fadeIn(js_fade_speed);
                }
                doneFade = true;
            }
        });
    } else {
        if (!$(elIn).is(':visible')) {
            $(elIn).fadeIn(js_fade_speed);
        }
    }
}

/**
 * ellipse() - ellipse a string to given length if it is too long.
 *
 * @param string value  - value to be ellipsed
 * @param int    maxLen - maximum length.
 *
 * @returns string - ellipsed value
 */
function ellipse(value, maxLen)
{
    if (value.length > maxLen) {
        return value.substr(0, maxLen - 3) + '...';
    }
    return value;
}


/**
 * GotoCategory - Navigate To a Category (handle differences btw home and rest of site)
 * @param int    categoryId
 */
function gotoCategory(categoryId){

    // Not on homepage
    if(typeof(Veri.Home) == "undefined"){
        return true; // Follow a href=""
    }
    // On the homepage
    else {
        // Load Via Ajax 
        return HashListener.rebuildHash({'_pg':1,'_ct':categoryId},true);
    }
}


/**
 * SpellCheck - handles spell checking for search queries'
 */
var SpellCheck = {
    // we have to increment this every time we use the spell checker so that
    // we have unique dom ids.
    count: 0,

    // this will be set by the check() function
    callback: null,

    /**
     * check() - check spelling.
     *
     * @param string   q        - query to check spelling for
     * @param function callback - callback for when results come back
     */
    check: function(q, callback)
    {
        SpellCheck.callback = callback;
        
        var src = 'http://search.yahooapis.com/WebSearchService/'
                + 'V1/spellingSuggestion?appid=YahooDemo&query=' + escape(q)
                + '&output=json&callback=SpellCheck.handleResponse';

        $script = $('<script id="spellCheck_' + SpellCheck.count + '"></script>');
        // create a <script> element for the yahoo to return the results into.
        document.getElementsByTagName('head')[0].appendChild($script.get(0));
        $script.attr('src', src);
    },
 
    /**
     * handleResponse() - callback to yahoo spell checker.
     *
     * @param object resObj - spell check results
     */
    handleResponse: function(resObj)
    {
        var result = (resObj.ResultSet && resObj.ResultSet.Result)
                   ? resObj.ResultSet.Result : false;

        // call callback
        SpellCheck.callback(result);
    }
}; // END namespace SpellCheck



/**
 * HashListener - listens for changes in location hash
 *
 * constructor - sets object variables
 *
 * @param function loadFunction - callback for when hash changes
 * @param object   options      - options: fields and their defaults.  Keys:
 *     fields     - associative array of accepted fields (keys) and their defaults (values)
 *     validators - optional array of regexps that the field must match (ignored otherwise)
 */
function HashListener(loadFunction, options)
{
    this.loadFunction = loadFunction;
    this.options      = options;
}

/*
 * static properties
 *
 * These functions forward action to the active instance of HashListener
 * (HashListener.listener)
 *
 * They provide an interface to the dynamic functions and allow functions to be
 * called from embedded HTML (ex: onchange="HashListener.rebuildHash({'_sw':'all'});")
 */
$.extend.call(HashListener, {

    // active listener
    listener  : null,

    /**
     * init() - initialises listener
     *
     * @params function loadFunction - passthrough to constructor
     * @params object   options      - passthrough to constructor
     * @params object   extension    - object to extend the listener instance with
     */
    init : function(loadFunction, options, extension)
    {
        HashListener.listener = new HashListener(loadFunction, options);

        if (extension) { // extend the hash listener object if argument exists
            $.extend.call(HashListener.listener, extension);
        }

        if (!options.dontRun) {
            HashListener.listener.testHash(true, false);
        }

        return HashListener.listener;
    },

    /**
     * getInstance()
     */
    getInstance: function() { return HashListener.listener; },

    /**
     * start() - starts hash listener
     */
    start: function() { HashListener.listener.testHash(true, false); },

    /**
     * refresh() - calls active instance of listener with refresh argument set to true
     */
    refresh : function() { HashListener.listener.testHash(false, true); },

    /**
     * rebuildHash() - dipatches to instnace of HashListener
     *                 this allows rebuild hash to be called from event handler
     *                 embedded in HTML (onchange="HashListener.rebuildHash({..})")
     *
     * @params  - passthroughs to object method
     * @returns - always false
     */
    rebuildHash : function(params, reset)
    {
        reset = (typeof(reset) == 'undefined') ? false : reset;
        try {

            // this could be an onsubmit or onclick callabck 
            // we want to return false even if there's an error
            HashListener.listener.rebuildHash(params, reset);
        } catch (e) {log(e);}

        return false;
    },

    /**
     * goHash() - sets location hash and calls active instance of listener
     */
    goHash: function(hash)
    {
        window.location.hash = hash;
        HashListener.listener.testHash(false, false);
    },

    validateChangedFields : function(fields, validators, opts, oldOpts)
    {
        var isDefault = true;
        var optsChanged = {};

        for (var i in fields) {

            // validator can be either regexp or function (or nonexistent)
            var validator = (validators && validators[i])
                          ? ((validators[i] instanceof RegExp) 
                            ? function(v) { return validators[i].test(v); }
                            : validators[i]) // just use the function
                          : function(v) { return true; }; // no validator

            if (typeof(opts[i]) != 'string' || !validator(opts[i])) {
                // if option doesn't exist or is invalid, use default
                opts[i] = fields[i];
            }

            if (opts[i] != fields[i]) { isDefault = false; }

            // take note of which options have changed
            optsChanged[i] = opts[i] != ((typeof(oldOpts[i]) != 'undefined')
                                        ? oldOpts[i]
                                        : fields[i]);
        }

        return { 'opts'        : opts,
                 'optsChanged' : optsChanged,
                 'isDefault'   : isDefault };
    },

    /**
     * getCurrentOpt() - gets the value of the current hash options specified in arguemnt
     *                   If there is no value, it will return the default value
     *
     * @param   string whichOpt - key of the option we're looking up the value for
     * @returns string - value of the option
     */
    getCurrentOpt: function(whichOpt, noDefault)
    {
        return HashListener.listener.getCurrentOpt(whichOpt, noDefault);
    },

    hashIsDefault: function()
    {
        return HashListener.listener.hashIsDefault();
    }
});

/*
 * dynamic properties
 *
 * Propterties can be added/overridden by HashListener.init
 */
HashListener.prototype = {

    // are we in test hash function
    inTestHash : 0,

    // set timeout timer id
    timerId    : 0,

    // last hash to compare with
    lastHash   : '',

    /*
     * Parse a hash string into an array of params
     * This is a dynamic property because it may be overridden as in the case of userPrefs
     *
     * i.e. hashData = /_vw=x/_pg=7/ => obj[_vw]=x, obj[_pg]=7
     */
    parseHash : function(hashData)
    {
        // Parse the hash
        var str = hashData.match(/^#?\/(.+?)\/$/);

        // Nothing In the hash return null
        if (!str) {
            return null;
        }
        // We items to process
        else{
            // Assuming all went well thus far, we continue processing:
            // 1) We create a new object to hold all these params.
            // 2) We split the keys and values into one big list.

            var opt = new Object();
            var arr = str[1].split('/');

            // If we have an uneven number of elements in the list (not evenly divisible by 2), we can also assume invalid data:
            if ((arr.length % 2) > 0) {

                return false;

            } else {

                // If we got this far, the next step is to convert our big list into key/value pairs.
                // The list is always starts with a key and ends with a value like this:
                //     key, value, key, value, key, value, key, value, etc
                // Knowing this we can say that all even indexes (0,2,4 etc) are keys, and all odd indexes (1,3,5 etc) are values.

                // We loop over all the even indexes to get the keys, and simply add 1 to each iteration to get the value.
                //     e.g. opt[arr[0]] = arr[1], opt[arr[2]] = arr[3], etc
                for (var i = 0; i < arr.length; i = i + 2) {
                    opt[arr[i]] = ($.browser.safari) ? unescape(arr[i + 1]) : arr[i + 1];
                }

                // Now the only thing left to do is return a neatly formatted array:
                return opt;

            }
        }
    },

    /*
     * callbacks : onNav         - called when a new hash is navigated to INTERNALLY
     *             onStateChange - called when change in hash is caught
     */
    onNav: function()               { return true; },
    attachOnNav: function(callback) { this.onNav = callback; },
    detachOnNav: function()         { this.onNav = function() { return true; }; },

    onStateChange: function()               { return true; },
    attachOnStateChange: function(callback) { this.onStateChange = callback; },
    detachOnStateChange: function()         { this.onStateChange = function() { return true; }; },

    /*
     * Rebuilds the current hash when something changes:
     * (current hash = url/#[stuff here])
     *
     * @param object newOpts - holding new parameters to put into hash
     * @param bool   reset   - reset params missing in newOpts to default?
     */
    rebuildHash : function(newOpts, reset)
    {
        var oldOpts = ((typeof(reset) == 'undefined') || !reset)
                    ? this.parseHash(window.location.hash.substr(1))
                    : {};

        var res;
        switch (typeof(res = this.onNav(newOpts, reset))) {
            case 'object':  var optArray = res; break;
            case 'boolean': if (!res) { return; } // else fallthrough
            default:        var optArray = arrayMerge(oldOpts, newOpts);
        }

        window.location.hash = '#' + this.encodeHash(optArray);

        // fire
        this.testHash(false, false);
    },

    /**
     * enchodeHash() - turns hash options into a string
     *
     * @param object optArray - hash options
     * @return string
     */
    encodeHash: function(optArray)
    {
        var optStr = '';

        for (var i in optArray)
        {
            // skip empty options
            if ((optArray[i] === null) && (optArray[i].length < 1)) { continue; }
            if (optArray[i] == this.options.fields[i]) { continue; }

            optStr += i + '/' + optArray[i] + '/';
        }

        return (optStr == '') ? '' : '/' + optStr;
    },

    /** 
     * testHash() - Timer function.  Tests if location has has changed and if so
     *              calls callback function (this.loadFunction) 
     *
     * @param bool firstRun 
     * @param bool refresh
     */
    testHash: function(firstRun, refresh)
    {
        // Dont want this looping multiple times if this is called manually
        // (i.e. like when called with refresh)
        clearTimeout(this.timerId);

        // make sure testHash not being run more than once at a time
        if (this.inTestHash == 1) {
            return;
        }

        this.inTestHash = 1;

        var anchor = window.location.hash.substr(1);

        // If this is the first run, or if we're refreshing (or loading for the first time)
        if (this.lastHash != anchor || firstRun || refresh) {

            var oldOpts = this.parseHash(this.lastHash);
            var opts    = this.parseHash(anchor);
            oldOpts     = (oldOpts) ? oldOpts : {};
            opts        = (opts)    ? opts    : {};

            this.lastHash = anchor;

            this.loadHash(firstRun, refresh, anchor, opts, oldOpts);
        }

        // Finished with testHash, let it run again later
        this.inTestHash = 0;
        var me = this;
        this.timerId = setTimeout(function(){ me.testHash(false); } ,500);
    },

    /**
     * loadHash() - Validates options and calls load function
     *              Called by testHash() on: 1) hash change; 2) refresh; 3) first run.
     *
     * @param bool firstRun
     * @param bool refresh
     * @param string anchor - new anchor
     * @param object opts
     * @param object oldOpts
     */
    loadHash: function(firstRun, refresh, anchor, opts, oldOpts)
    {
        // Set this first so loadHash is not called multiple times while loading
        var validated = HashListener.validateChangedFields(this.options.fields,
                                                           this.options.validators,
                                                           opts,
                                                           oldOpts);

        $('input[hashOpt]').each(function() {
            $(this).val(validated.opts[$(this).attr('hashOpt')]);
        });
        $('select[hashOpt]').each(function() {
            $(this).selectByValue(validated.opts[$(this).attr('hashOpt')]);
        });

        // call the callback provided to us in constructor
        this.onStateChange();

        this.loadFunction(validated.opts,
                          firstRun,
                          refresh,
                          validated.isDefault,
                          validated.optsChanged);
    },

    /**
     * getCurrentOpt() - gets the value of the current hash options specified in arguemnt
     *                   If there is no value, it will return the default value
     *
     * @param   string whichOpt - key of the option we're looking up the value for
     * @returns string - value of the option
     */
    getCurrentOpt: function(whichOpt, noDefault)
    {
        var hash = this.parseHash(window.location.hash);

        if (typeof(this.options.fields[whichOpt]) == 'undefined') {
            return null;
        }

        return (hash && typeof(hash[whichOpt]) != 'undefined')
             ? hash[whichOpt]
             // if the option isn't set in the location hash we return the default value
             // unless the noDefault argument is set
             : ((noDefault) ? null : this.options.fields[whichOpt]);
    },

    hashIsDefault: function()
    {
        var hash = this.parseHash(window.location.hash);

        for (var i in hash) {
            if (hash[i] != this.options.fields[i]) { return false; }
        }
        return true;
    }

}; // END namespace HashListener

$(function() {
    preloadImages([
                   'loading.v0.gif', // loading image
                   // personalization buttons
                   'nav_personalized_off.v0.gif',
                   'nav_personalized_on.v0.gif',
                   'grid-headerOver.v0.gif',

                   // for popups
                   'popupImages.v1.png',
                   'popupImages_topbottom.v0.png',
                   'popupImages_leftright.v0.png',

                   // topic popup
                   'topicSearchButton.v0.gif',
                   'feed-icon-16x16.v0.gif',
                   'linkGoIcon.v0.gif',
                   'viewOpinionTopBar.v0.gif',
                   'user-content-form.v0.gif',
                   'topic-first-write-a.v0.gif',
                   'user-content-rank.v0.gif',
                   'pollSummaryBG.v0.jpg',
                   'pollSummaryBGSelected.v0.jpg',
                   'bar_left_red.v0.gif',
                   'bar_left_black.v0.gif',
                   'bar_middle_red.v0.gif',
                   'bar_middle_black.v0.gif',
                   'bar_middle_green.v0.gif',
                   'bar_right_black.v0.gif',
                   'bar_right_green.v0.gif',
                   'grid-header.v0.gif',
                   'category-content-rank.v0.gif',
                   'user-content-vote-p.v0.gif',
                   'opinion-content-a-report.v0.gif',
                   'topic-first-edit-a.v0.gif',
                   'rank_icon_tiny_1.v0.gif',
                   'rank_icon_tiny_2.v0.gif',
                   'rank_icon_tiny_3.v0.gif'
                  ]);
});


/*
function showEl() {
    var el = $($('#findWhat').val());

    $('#count').html(el.length.toString(10)).css('background', 'blue');
    var save = el.css('background');
    el.css('background', 'red');
    

    setTimeout(function() { el.css('background', save); $('#count').css('background', 'transparent'); }, 1500);

}

$(function() {
    $('<div style="width:200px; height:50px; top:0; position:fixed; background:green; z-index:1000001;">'
      + '<form onsubmit="showEl(); return false;">'
      + '<input id="findWhat" />'
      + '<input type="submit" value="clickme" />'
      + '<p id="count">count</p>'
      + '</form></div>').appendTo('body');
});
*/



/* ********************* */
/* Kickstart the Process */
/* ********************* "*/

var Kickstart = {
    bundles: new Object,
    isIBuilt: false,
    needsSaving: false,
    closingStarted: false,
    
    _build: function(xml)
    {
       $("#kickstart_content").empty();
       var response = xml.responseXML.documentElement;
       var parents = $(response).children();

       var column;
       for (var i=0; i < parents.length; i++)
       {
           var parent = $(parents[i]);
           var column = $(document.createElement("div"));
           column.addClass('kickstart_column');
           $("#kickstart_content").append(column);
           var cat_top = $(document.createElement("DIV"));
           var cat_top_contents = $(document.createElement("DIV"));


           cat_top.addClass('kickstart_cat_top');
           cat_top_contents.addClass('kickstart_cat_contents');

           cat_top_contents.text(parent.attr('title'));
           var parent_id = parent.attr('id');

           cat_top.bind('mouseover', { id: parent_id }, Kickstart._hover);
           cat_top.bind('mouseout', {id: parent_id }, Kickstart._moveOut);
           cat_top.bind('click', {id: parent_id}, Kickstart._parentClick);

           Kickstart.bundles[parent_id] = {object: cat_top, children: new Array(), picked: false};

           cat_top.append(cat_top_contents);
           column.append(cat_top);

           var childs = $(parents[i]).find("children").children();
           for( var j = 0; j < childs.length; j++ )
           {
               var child = $(document.createElement("DIV"));
               var insideChild = $(document.createElement("DIV"));
               var bundle = $(childs[j]);
               var bundle_id = bundle.attr('id'), picked = (bundle.attr('picked') == 'true' ? true : false);

               child.addClass('kickstart_cat');
               insideChild.addClass('kickstart_cat_contents');

               child.bind('mouseover', { id: bundle_id }, Kickstart._hover);
               child.bind('mouseout', {id: bundle_id }, Kickstart._moveOut);
               child.bind('click', {id: bundle_id}, Kickstart._click);

               Kickstart.bundles[bundle_id] = {picked: picked, object: child, id: bundle_id, parent: parent_id};
               Kickstart.bundles[parent_id].children.push(bundle_id); 

               if (picked) child.css('background-position', 'center right');
 
               insideChild.text($(childs[j]).attr('title'));
               column.append(child);
               child.append(insideChild);
           }
           var child_id = $(childs[0]).attr('id');
           Kickstart._checkParent(child_id);
       }
       column.addClass('kickstart_lastColumn');
       $("#kickstartPop").jqmShow();
    }, 

    build: function(needsSaving)
    {
        // Generate a Random Number
        var rnd  = Math.round(Math.random()*1000000);
        Kickstart.needsSaving = (needsSaving) ? true : false;


        $.ajax({ type: "GET", 
                 url: base_url + '/kickstart/gather/' + rnd,
                 dataType: 'xml',
                 complete: function(xml)
                 {
                     if(xml.responseXML)
                         Kickstart._build(xml);                
                 }
               });
    },

    submit: function()
    {
        var picked = new Array();
        for(var i in Kickstart.bundles)
        {
            if(!Kickstart.bundles[i].children)
               if(Kickstart.bundles[i].picked)
                   picked.push(Kickstart.bundles[i].id);
        }

        var query = "", started = false;
        for(var i in picked)
        {
           query += (started ? "&" : "") + "picked[]="+picked[i];
           started = true;
        }

        // Generate a Random Number
        var rnd  = Math.round(Math.random()*1000000);

        var alertType = '', alert = '';
        if(query != "") {
            pageTracker._trackPageview('/action/kickstart_complete/');
            alertType = 'success';
            alert = 'Kickstart Successful! Veritocracy will continue to personalize itself as you read and vote.';
        }
        else {
            pageTracker._trackPageview('/action/kickstart_skipped/');
            alertType = 'warn';
            alert =  "Kickstart Skipped! Don't worry, Veritocracy will still personalize itself as you read and vote.";
        }

        var reload = Kickstart.needsSaving;

        Kickstart.needsSaving = false; 

        if(alertType != '' && alert != '')
            setTimeout('Veri.Alert.triggerAlert("'+alertType+'", "'+alert+'")', 750);

        $.ajax({ type: "POST", 
                 url: base_url + "/kickstart/save/"+rnd,
                 data: query, 
                 complete: function() {
                     if(!Kickstart.closingStarted)
                         $("#kickstartPop").jqmHide(); 
                     if(reload && query != "") setTimeout('_log_in_out_data_refesh()',250); 
                 }});
    },

    _checkParent: function(child)
    {
        var parent = Kickstart.bundles[Kickstart.bundles[child].parent];
        var everything = true;
        for( var i in parent.children)
        {
            if(!Kickstart.bundles[parent.children[i]].picked)  
            {
                everything = false;
                break;
            }         
        }
        parent.object.css('background-position', (everything ? 'center right' : 'center center') );
        parent.picked = everything;
    },

    _hover: function(event)
    {
        if (!Kickstart.bundles[event.data.id].picked)
            $(this).css('background-position', 'center left');
    },

    _moveOut: function(event)
    {
        if(!Kickstart.bundles[event.data.id].picked)
            $(this).css('background-position', 'center center');
    }, 

    _click: function(event)
    {
       var picked = Kickstart.bundles[event.data.id].picked;
        $(this).css('background-position', (picked ? 'center left' : 'center right') );
        Kickstart.bundles[event.data.id].picked = !picked;
        Kickstart._checkParent(event.data.id);
    },
  

    _parentClick: function(event)
    {
      var picked = Kickstart.bundles[event.data.id].picked;
      for(var i=0; i < Kickstart.bundles[event.data.id].children.length; i++)
      {
          var bundle = Kickstart.bundles[Kickstart.bundles[event.data.id].children[i]];
          if(bundle.picked == picked)
          {
              bundle.object.click();
              if(!bundle.picked)
                  bundle.object.css('background-position', 'center center');
          }
       }
      Kickstart.bundles[event.data.id].picked = !picked;
      $(this).css('background-position', (picked ? 'center left' : 'center right') );
    },

    submitHover: function(but)
    {
       $(but).toggleClass('kickButton');
       $(but).toggleClass('kickButtonHover');
    }
};

function slickMouseover(item)
{
    $(item).addClass('over');
}

function slickMouseout(item)
{
    $(item).removeClass('over');
}


/*
 *
 * Veri.ArticleSubmit handles the article submitting buttons
 */

if (Veri.ArticleSubmit == null || typeof(Veri.ArticleSubmit) != "object") { Veri.ArticleSubmit = new Object(); }


Veri.ArticleSubmit = {
    concat_default: 44,
    small_width: 113,
    link_class:  'h3 a',
    topic_class: '.topic_title, .topic_title_u',
    small_class: '.submit_write_small',

    small_over: function(item) {
        var     len   =         (arguments[1]) ? arguments[1] : this.concat_default,
                a     =         $(item).find(this.link_class), 
                b     =         $(item).find(this.topic_class), 
                image =         $(item).find(this.small_class);

        b.width(b.width() - this.small_width);
        a.html(ellipse(a.html(), len));
        image.width(this.small_width);
        $(image).css('background-position', 'top right');
    },

    small_out: function(item)  {
        var    len = (arguments[1]) ? arguments[1] : this.concat_default,
               a = $(item).find(this.link_class), 
               b=$(item).find(this.topic_class), 
               image = $(item).find(this.small_class);

        b.width(b.width() + this.small_width);
        a.html(ellipse(a.attr('title'), len));
        image.width(1);
        $(image).css('background-position', 'top left');
    },

    topic_submit: function(topic) {
        return canPostOpinion("topic="+topic);
    },

    opinion_reply: function(topic, reply)
    {
        return canPostOpinion("topic="+topic+"&reply="+reply);
    }
};

/* End Veri.ArticleSubmit */

function elipse(string, toLen)
{
    if(string.length <= toLen) return string;
    return string.substr(0, toLen-3) + "...";

}

