//set up jQuery $() functionality in noConflict mode using $j() -- $() belongs to Prototype
var $j = jQuery.noConflict();

var the_version = $j.browser.version.substr(0,3); //get the browser version

if ($j.browser.msie || $j.browser.mozilla && the_version < 1.9) { // if browser is IE or FF2
  var browser_is_old;
}

// Image Rollover/off Functions (Added 3/12/08 by wil1) 
// Made so that all you have to do to is give an image a rollover function is add the class "rollover_img" 
function img_over(element_id) {
  $j("#"+element_id).attr("src", $j("#"+element_id).attr("src").split('_off').join('_over'));
}
function img_off(element_id) {
  $j("#"+element_id).attr("src", $j("#"+element_id).attr("src").split('_over').join('_off'));
}

// Image Wait/Go Functions (Added 3/30/08 by wil1) 
// Made so that all you have to do to is give an image/button a disable function is add the class "wait_img"
function img_wait(element_id) {
  $j("#"+element_id).attr("src", $j("#"+element_id).attr("src").split('_over').join('_wait'));
  $j("#"+element_id).attr("disabled", "disabled");
}
function img_go(element_id) {
  $j("#"+element_id).attr("src", $j("#"+element_id).attr("src").split('_wait').join('_off'));
  $j("#"+element_id).removeAttr("disabled"); 
}

// Double Swap Function (Added 3/12/08 by wil1) 
// Use on elements where you want the displayed div & selected class swapped.
function double_swap(new_select, swap_class) {
  var cur_select = $j("."+swap_class).attr("id"); //the id of the element with the swap_class class
  $j("#"+cur_select).removeClass(swap_class); 
  $j("#"+new_select).addClass(swap_class);

  $j("#ajax_"+cur_select).fadeOut(500, function(){
    $j("#ajax_"+new_select).fadeIn(500);
  });
}

// Update Status Bar w/tweening animation! (Added to photoset_uploader.js 3/22/08 by wil1 and moved here)
// Use to update the width of a div which represents the progress of something. update_status_bar(83.69, "upload_progress_div", 550)
function update_status_bar(percent, div, total_width) {
  var pixels_per_point = total_width/100;
  var new_width = percent * pixels_per_point;
  $j("#"+div).animate( { width: new_width }, 800, 'easeIn');
}

// Set Cookie Function (Added to utils.js by jm3 and moved here)
// (REWRITE IN jQuery) - Use to add a cookie to the user's browser
function set_cookie(name, value, days) {
  var expires;
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    expires = "; expires="+date.toGMTString();
  } else {
    expires = "";
  }
  document.cookie = name+"="+value+expires+"; path=/";
}

// Get Cookie Function (Added to utils.js by jm3 and moved here)
// (REWRITE IN jQuery) - Use to check for a cookie
function get_cookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') {
      c = c.substring(1,c.length);
    } 
    if (c.indexOf(nameEQ) === 0) {
      return c.substring(nameEQ.length,c.length);
    }
  }
  return null;
}

// Clear Cookie Function (Added to utils.js by jm3 and moved here)
// Use to remove a cookie
function clear_cookie(name) {
  set_cookie(name,"",-1);
}

// Console Log Function (Added to utils.js by jm3? and moved here)
// Use to print logs to the console
function log(s) {
  if (document.all) { 
    alert(s);
  } else {
    console.log(s);
  }
}

// Toggle Div Function (Added to utils.js by wil1 and moved here)
// Use to toggle the div on and off
function toggle_div(id) {
  $j("#"+id).toggle();
}

// Class Swap Function (Added 3/11/08 by wil1) 
// Use on elements which need to have a selected class swapped.
function class_swap(new_select, swap_class) {
  var cur_select = $j("."+swap_class).attr("id"); //the id of the element with the swap_class class
  $j("#"+cur_select).removeClass(swap_class); 
  $j("#"+new_select).addClass(swap_class);
}

// Div Swap Function (Added 3/12/08 by wil1) 
// Use on elements where you want the displayed div swapped.
function div_swap(new_select, swap_class) {
  var cur_select = $j("."+swap_class).attr("id"); //the id of the element with the swap_class class

  $j("#ajax_"+cur_select).fadeOut('slow', function(){
    $j("#ajax_"+new_select).fadeIn('slow');
  });
}

// Blur Input Function (Added 5/28/08 by wil1)
// Use on input fields to replace the removed text (from clear_input) if the user has not filled it in
function blur_input(field_id) {
  var text = $j("#"+field_id).val(); // get the value of #field_id
  var old_text = $j("#"+field_id+"_hidden").val(); // get the value of #field_id_hidden which contains the original contents of #field_id
  
  if (text !== "") { // if the value is not blank then the user has begun filling in the field
    $j("#"+field_id).unbind("blur"); 
  } else { // otherwise it needs to be reverted
    $j("#"+field_id).val(old_text).unbind("blur").bind("click", function(){
      clear_input(field_id);
    }); 
    
    if (field_id == "relationship_text") { // on the profile friending preview we have another field which must change too
      $j("#friending_text").html(old_text); 
    } else if (field_id == "status_update_text") { 
      $j('#send_button').attr("disabled", "disabled");
    }
    
  }

}

// Clear Input Function (Added 5/28/08 by wil1)
// Use on input fields to empty out initial text on click
function clear_input(field_id) {
  $j("#"+field_id).val("").unbind("click").bind("blur", function(){ // set the value to "", unbind the click fn, and bind the blur fn
    blur_input(field_id); // on blur run this function
  });
  
  if (field_id == "relationship_text") { // on the profile friending preview we have another field which must change too
    $j("#friending_text").empty();  
  } else if (field_id == "status_update_text") {
    $j('#send_button').removeAttr("disabled").bind("click", function(){
      submit_status_form();
    });
  }
}

// Load Dynamic CSS/JS File (Added 6/10/08 by wil1) 
// Use to load a css or js file on the fly 
function load_file(file, type) { 
  var fileref;
  if (type=="js") { //if filename is a external JavaScript file 
    fileref=document.createElement('script'); 
    fileref.setAttribute("type","text/javascript"); 
    fileref.setAttribute("src", file); 
  } else if (type=="css") { //if filename is an external CSS file 
    fileref=document.createElement("link"); 
    fileref.setAttribute("rel", "stylesheet"); 
    fileref.setAttribute("type", "text/css"); 
    fileref.setAttribute("href", file); 
  } 
  if (typeof fileref!="undefined") { 
    document.getElementsByTagName("head")[0].appendChild(fileref); 
  } 
}

jQuery.preloadimage = function() {
  for(var i = 0; i < arguments.length; i++) {
    jQuery("<img>").attr("src", arguments[i]);
  }
};


/* actions.js
   This file leverages the functions in buddy.js to create magic classes for simple universal/unobtrusive 
   javascript functionality. This file is the result of the pairing down of the original utils.js into reusable
   functions as well as the drive to remove js from the xhtml markup. */

// This code checks the document and waits until it's ready to be manipulated
$j(document).ready( function(){

  $j('#processor').change(function(){
    var processor = $j(this).val();
    if (processor == 'credit') {
      $j('#subscribe_by_credit').slideDown();
    } else if (processor == 'paypal') {
      $j('#subscribe_by_credit').slideUp();
    }
  });

  $j('#search-field').click(function(){
    $j('#search-field').val('').unbind('click');
  })
  
  if ($j.browser.msie && $j.browser.version === 6) {
    if (show_ie6_banner === true) {
      $j('#modal_overlay').fadeIn('fast');
      var screen_mid   = ($j(window).width() / 2) - 365;
      $j('#unsupported_browser').css('left', screen_mid).fadeIn('fast');
    }
  }
  
  $j('#continue_ie6').click(function(){
    $j('#modal_overlay').fadeOut('fast');
    $j('#unsupported_browser').fadeOut('fast');
    set_cookie('ie6_banner', 'hidden', 7);
  });
      
  $j('.show_buy_votes').click(show_buy_more_votes); // show the buy votes menu
  $j('#btn_up').click(votes_up); // incriment the votes up on the buy votes modal
  $j('#btn_down').click(votes_down); // incriment the votes down on the buy votes modal
  $j('#votes_cancel_btn').click(votes_cancel_btn); // close buy votes modal
  $j('div.round').corner('7px cc:#111310');
  $j('div.round_10').corner('10px cc:#111310');
  $j('div.round_10_t').corner('10px cc:#111310');
  $j('div.top_rounded').corner('10px top cc:#111310');
  $j('div.rounded').corner('7px cc:#111310');
  $j('div.rounded2').corner('7px cc:#111310');
  $j('div.rounded3').corner('5px cc:#111310');
  $j('div.rounded4').corner('5px cc:#111310');
  $j('div.rounded5').corner('5px cc:#111310');

  // universal image rollover - just add class="rollover_img"
  $j('.rollover_img').hover( function() {       
    img_over($j(this).attr('id'));
  }, function() { // on "un-hover"
    img_off($j(this).attr('id'));
  });
  
  // universal image wait function - just add class="wait_img"
  $j(".wait_img").click( function() {
    img_wait($j(this).attr("id"));
    setTimeout(img_go($j(this).attr("id")), 5000);
  });
  
  // universal button disable+submit - just add class="smart_submit"
  $j('input.smart_submit').click( function(){
    $j('input.smart_submit').attr('disabled', 'disabled');
    $j(this).parents('form').submit();
  });
  
  // universal button disable w/o submit - just add class="click_disable"
  $j('input.click_disable').click( function(){
    $j(this).attr('disabled', 'disabled');
  });
  
  // universal function that toggles the "selected_menu_item" class and swaps div visibilty
  $j('.feed_swap').click( function(){ 
    double_swap($j(this).attr('id'), 'selected_menu_item'); 
    track_page_view('TAB_SWITCH');
  });
  
  // a second double_swap on the same page made this necissary
  $j('.set_swap').click( function(){ 
    double_swap($j(this).attr('id'), 'selected_item'); 
  });
  
  // a universal checkbox select all function
  $j('.select_all').click( function(){
    $j('.selectable').attr("checked", "checked");
  });
  
  // a universal checkbox de-select all function
  $j('.select_none').click( function(){
    $j('.selectable').removeAttr("checked");
  });
  
  $j('#close_global_note').click(function(){
    toggle_div( 'expiration_warning' );
    set_cookie( 'hide_expiration_warning_<%= num_days %>', 1, 30);
  });
  
  $j('#close_fan_dashboard_banner').click(function(){
    toggle_div('fan_dashboard_banner');
    set_cookie('fan_dashboard_banner', 'hidden', 60);
  });

  $j('.artist_cancel_button').click(function(){
    toggle_div($j(this).attr('alt'));
  });
  
  $j('#plans_summary_open').click(function(){
    $j("#plans_summary").show();
    $j("#plans_summary_close_link").show();
    $j("#plans_summary_open_link").hide();
  });
  
  $j('#plans_summary_close').click(function(){
    $j("#plans_summary_text").hide();
    $j("#plans_summary_close_link").hide();
    $j("#plans_summary_open_link").show();
  });
  
  $j('#roles_summary_open').click(function(){
    $j("#roles_summary").show();
    $j("#roles_summary_close_link").show();
    $j("#roles_summary_open_link").hide();
  });
  
  $j('#roles_summary_close').click(function(){
    $j("#roles_summary_text").hide();
    $j("#roles_summary_close_link").hide();
    $j("#roles_summary_open_link").show();
  });
  
  $j('#paypal_btn').click( function() {
    track_page_view('PURCH_VOTES_CLICK_PAYPAL'); // was: PURCH_VOTES_CLICK
  });
  
  $j('#use_credit_card').click(function(){
    if ($j.browser.msie) {
      $j('#buy_votes_quantity').fadeOut('fast', function(){ // fade this out so it doesn't hang off the edge on slideUp
        $j('#buy_votes_quantity_form').slideUp('slow', function(){
          $j('#buy_votes_card_form').slideDown('slow');
          $j('#buy_votes_footer').fadeOut('fast');
        });
      });
    } else {
      $j('#buy_votes_quantity_form').slideUp('slow', function(){
        $j('#buy_votes_card_form').slideDown('slow');
        $j('#buy_votes_footer').fadeOut('fast');
      }); 
    }
    track_page_view('PURCH_VOTES_CLICK_CC');
  });
  
  $j('#roll_back_form').click(function(){
    if ($j.browser.msie) {
      $j('#order_total').fadeOut('slow'); // fade this out so it doesn't hang off the edge on slideUp
      $j('#buy_votes_card_form').slideUp('slow', function(){
        $j('#buy_votes_quantity_form').slideDown('slow', function(){
          $j('#buy_votes_quantity').fadeIn('fast'); //fade this in AFTER the slideDown so it doesn't hang
          $j('#buy_votes_footer').fadeIn('fast');
          $j('#order_total').fadeIn('fast'); //reset this as it doesn't look bad on slideDown just up
        });
      });
    } else {
      $j('#buy_votes_card_form').slideUp('slow', function(){
        $j('#buy_votes_quantity_form').slideDown('slow');
        $j('#buy_votes_footer').fadeIn('fast');
      });
    }
  });
  
  $j('#buy_votes_close').click(votes_cancel_btn);
  $j('#thanks_close').click(thanks_cancel_btn);
  
  var container = $j('#validation_error');    
	var validator = $j("#buy_votes_cc_form").validate({
	  errorContainer: container,
		rules: {
			billing_name: { required: true, minlength: 4 },
			credit_card_number: { required: true, minlength: 15, creditcard: true },
			exp_month: "required",
			exp_year: "required",
			ccv: { required: true, minlength: 3, maxlength: 4 },
			address_1: { required: true, minlength: 5 },
			city: { required: true, minlength: 3 },
			state: { required: true, minlength: 2 },
			zip: { required: true, minlength: 3 },
			country: "required"
		},
		// the errorPlacement has to take the table layout into account
		errorPlacement: function(error, element) {
      error.appendTo('#invalid_list');
		}
	});
	
	$j('#buy_now').mouseup(function(){
	  if ($j('.error').length === 0 && $j('#billing_name').val().length !== 0 && $j('#credit_card_number').val().length !== 0  && $j('#exp_month').val().length !== 0 && $j('#exp_year').val().length !== 0 && $j('#ccv').val().length !== 0 && $j('#address_1').val().length !== 0 && $j('#city').val().length !== 0 && $j('#state').val().length !== 0 && $j('#country').val().length !== 0) {
	    setTimeout('$j("#buy_now").attr({src: "/images/buttons/btn_submitting.jpg", disabled: true})', 150);
	  }
	});
	
	/* Status Validation
-------------------------------------------------------------------------------------- */  
  
  var status_container = $j('#validation_error');    
	var validator = $j("#status_form").validate({
	  errorContainer: status_container,
		rules: {
			status_update_text: { required: true, minlength: 4 }
		},
		// the errorPlacement has to take the table layout into account
		errorPlacement: function(error, element) {
		},
		submitHandler: function(){
		  var status_string = $j('#status_update_text').val();
      $j.ajax({ type: "POST", url: "/user/status/create", data: "status_text=" + status_string, 
        success: function(data) { 
          if (data == 'success') {
            track_page_view( 'STATUS_EDIT');
            $j('p#user_quote').empty().html('Now you&rsquo;re &ldquo;' + status_string + '&rdquo;');
            $j('#update_status').fadeOut('fast', function(){
              $j('#status').fadeIn('fast');
            });
          } else {
            $j('p#user_quote').empty().html('THERE WAS AN ERROR UPDATING YOUR STATUS.');
            $j('#update_status').fadeOut('fast', function(){
              $j('#status').fadeIn('fast');
            });

          }
        
        }
      
      });

		}
	});
	
	$j('ul#credit_card_form li input').focus(function(){
	  if ($j(this).attr('id') == 'buy_now') {
	    return;
	  } else {
	    $j(this).addClass('current_field');
	  }
	});
	
	$j('ul#credit_card_form li input').blur(function(){
	  if ($j(this).attr('id') == 'buy_now') {
	    return;
	  } else {
  	  $j(this).removeClass('current_field');
	  }
	});
	
	$j('ul#credit_card_form li select').focus(function(){
	  $j(this).addClass('current_field');
	});
	
	$j('ul#credit_card_form li select').blur(function(){
	  $j(this).removeClass('current_field');
	});
	
	$j('p#whats_this').click(function(e) { // ccv hover-menu			
		$j('#ccv_drop').css("left", (e.pageX - 250) +'px');
		$j('#ccv_drop').css("top", (e.pageY - 50) +'px');
		$j('#ccv_drop').fadeIn('fast');
  });

	
	$j('#ccv_drop').hover(function() {	
		$j('#ccv_drop').addClass("imOver"); // stamp it	
	},function(){
		$j('#ccv_drop').fadeOut('fast'); // make the hover menu go away!	
		$j('#ccv_drop').removeClass("imOver"); // stamp it	
	
	});
	
  // shows the public nav
  $j('#show_login').click(function(){
    $j('#head_text').fadeOut('fast', function(){
      $j('#pp_login_form').fadeIn('fast');
    });
  });
  
  $j('.give_play_btn').each( function(){ 
    
    var the_id = $j(this).attr('id');
    var num_only = $j(this).attr('id').split('vid_thumb_')[1];
    
    /*if ($j.browser['mozilla']) {
      var add_on = 1;
    } else {
      var add_on = 0;
    }*/
    
    $j('#clone_play_btn').clone()
      .attr({id:  'play_btn_' + num_only})
      .css({left: $j('#'+the_id).position().left + 44, top: $j('#' + the_id).position().top + 29})
      .insertAfter('#'+the_id)
      .fadeIn('fast');

  });

  $j('.give_play_btn_2').each( function(){ 
    
    var the_id = $j(this).attr('id');
    var num_only = $j(this).attr('id').split('vid_thumb_')[1];
    
    /*if ($j.browser['mozilla']) {
      var add_on = 1;
    } else {
      var add_on = 0;
    }*/
    
    $j('#clone_play_btn').clone()
      .attr({id:  'play_btn_' + num_only})
      .css({left: $j('#'+the_id).position().left + 35, top: $j('#' + the_id).position().top + 22})
      .insertAfter('#'+the_id)
      .fadeIn('fast');

  });
  
  $j('#zivity_search').click(function(){
    $j('#zivity_search').val('').unbind('click');
  });
  
  $j('#edit_status').click(function(){
    $j('#status').fadeOut('fast', function(){
      $j('#update_status').fadeIn('slow');
    });
  });
  
  

  
});

$j(window).load( function(){
  
  
});

$j(window).bind('resize', function() {
  resize_buy_more_votes();
});


/* page_specific.js
   This file uses the body id to serve one-off functions unobtrusively. This file is the result of pulling
   javascript out of the XHTML markup. */

$j(document).ready( function(){

  var body_id = $j("body").attr("id");    

  /* artist/photoset/show actions */
  if (body_id == "photoset") { 

    $j("#nav_next_thumb").hover(function(){ img_over("fwd_btn"); },function(){ img_off("fwd_btn"); });
    $j("#nav_back_thumb").hover(function(){ img_over("back_btn"); },function(){ img_off("back_btn"); });
  
    // Set a cookie for the photoset viewer based on what icon view the user used last 
    $j('#view_all_link').click( function(){
      set_cookie("photoset_view_preference", 1, 365);
    });
    // Remove a cookie for the photoset viewer based on what icon view the user used last 
    $j('#show_back_next_nav_link').click( function(){
      set_cookie("photoset_view_preference", 0, 365);
    });
  
    $j('#nav_next_thumb').click(fade_next);
    $j('#fwd_btn').click(fade_next);
    $j('#nav_back_thumb').click(fade_back);
    $j('#back_btn').click(fade_back);   
    
  /*} else if (body_id == 'dashboard_index' || body_id == 'accounts_privacy') {
  
    if ($j.browser.mozilla == true) {
      $j('ul#zivity_navigation').css({marginTop: '-10px'});
    }*/

  } else if (body_id == 'profile_photos_new' || body_id == 'profile_photos_create') {
    
    var the_version = $j.browser.version.substr(0,3);
    if (window.navigator.userAgent.indexOf('Win') > 0) {
      if ($j.browser.mozilla && the_version < 1.9) { //if we are in FF2 Windows
        $j('#fake_icon').css('width', '180px');
        $j('#fake_profile').css('width', '180px');
      } else if ($j.browser.mozilla && the_version >= 1.9) {
        $j('#fake_icon').css('width', '190px');
        $j('#fake_profile').css('width', '190px');
      }

    }
    
    $j('#icon_upload').css({left: $j('#fake_icon').position().left, top: $j('#fake_icon').position().top});
    
    $j('#icon_upload').change(function(){
      the_val = $j('#icon_upload').val();
      $j('#fake_icon').val(the_val);
    });
    
    $j('#profile_upload').css({left: $j('#fake_profile').position().left, top: $j('#fake_profile').position().top});
    
    $j('#profile_upload').change(function(){
      the_val = $j('#profile_upload').val();
      $j('#fake_profile').val(the_val);
    });

    if ($j.browser.msie) { // if browser is IE
      $j('#fake_icon').css('width', '200px');
      $j('#fake_profile').css('width', '200px');
      
    } else if ($j.browser.safari) {
      $j('#fake_icon_btn').hide();
      $j('#fake_profile_btn').hide();
      $j('#icon_upload').css({left: $j('#fake_icon').position().left + 300 + 'px', top: $j('#fake_icon').position().top + 2 + 'px', opacity: 1});
      $j('#profile_upload').css({left: $j('#fake_profile').position().left + 300 + 'px', top: $j('#fake_profile').position().top + 2 + 'px', opacity: 1});
    }
    
  }
  
});


var new_votes; var screen_mid_x; var popup_height; var popup_width; var new_position_x;

//down arrow for the buy votes incrimentor
function votes_down() {
  var buy_votes = parseInt($j("#purchase_num_votes").val(), 10);
  
  if (buy_votes > 5) { 
    new_votes = buy_votes - 5; 
  } else { 
    new_votes = 5; 
  }
  
  $j("#purchase_num_votes").val(new_votes);
  $j('#faux_votes').empty().prepend(new_votes);
  $j("#cc_num_votes").val(new_votes);
  $j('#cc_total').empty().prepend(new_votes);	  
}

//up arrow for the buy votes incrimentor
function votes_up() {
  var buy_votes = parseInt($j("#purchase_num_votes").val(), 10);
  var new_votes = buy_votes + 5;
  
  $j("#purchase_num_votes").val(new_votes);
  $j('#faux_votes').empty().prepend(new_votes);
  $j("#cc_num_votes").val(new_votes);
  $j('#cc_total').empty().prepend(new_votes); 
}

function get_position_buy_votes() {
  if ($j('body').attr('id') == 'photoset') {
    screen_mid_x   = $j(window).width() / 2;
    popup_height   = $j('#buy_votes').outerHeight();
    popup_width    = $j('#buy_votes').outerWidth();
    new_position_x = screen_mid_x - ( popup_width / 2) + 85; // centered on the photoset viewer :: adding 85 px nullifies the offset from the left area on the photoset viewer
    close_position_x = new_position_x + $j('#buy_votes').outerWidth() - 20;
    return { x: new_position_x, c_x: close_position_x };
  } else {
    screen_mid_x   = $j(window).width() / 2;
    popup_height   = $j('#buy_votes').outerHeight();
    popup_width    = $j('#buy_votes').outerWidth();
    new_position_x = screen_mid_x - ( popup_width / 2); // centered on the photoset viewer :: adding 85 px nullifies the offset from the left area on the photoset viewer
    close_position_x = new_position_x + $j('#buy_votes').outerWidth() - 20;
    return { x: new_position_x, c_x: close_position_x };
  }
  
}

function get_position_thanks() {
  if ($j('body').attr('id') == 'photoset') {
    screen_mid_x   = $j(window).width() / 2;
    popup_height   = $j('#buy_votes_thanks').outerHeight();
    popup_width    = $j('#buy_votes_thanks').outerWidth();
    new_position_x = screen_mid_x - ( popup_width / 2) + 85; // centered on the photoset viewer :: adding 85 px nullifies the offset from the left area on the photoset viewer
    close_position_x = new_position_x + $j('#buy_votes_thanks').outerWidth() - 20;
    return { x: new_position_x, c_x: close_position_x };
  } else {
    screen_mid_x   = $j(window).width() / 2;
    popup_height   = $j('#buy_votes_thanks').outerHeight();
    popup_width    = $j('#buy_votes_thanks').outerWidth();
    new_position_x = screen_mid_x - ( popup_width / 2); // centered on the photoset viewer :: adding 85 px nullifies the offset from the left area on the photoset viewer
    close_position_x = new_position_x + $j('#buy_votes_thanks').outerWidth() - 20;
    return { x: new_position_x, c_x: close_position_x };
  }
  
}

function form_reset() {
  $j('#buy_votes_card_form').hide();
  $j('#buy_votes_quantity_form').show();
  $j('#buy_votes_footer').show();
  $j("#purchase_num_votes").val(10);
  $j("#cc_num_votes").val(10);
  $j('#faux_votes').empty().prepend(10);
  $j('#cc_total').empty().prepend(10);
  vote_button_open = 0;
}

function votes_cancel_btn() {
  if (document.URL.charAt(4) == 's') {
    var redirect = 'http' + document.URL.split('https')[1];
    window.location = redirect;
    return;
  } else {
    $j("#buy_votes").fadeOut('fast');
    $j("#buy_votes_close").fadeOut('fast');

    $j('#buy_votes').css({left: '-2000px', top: '-2000px'});
    $j('#buy_votes').show();
    $j('#buy_votes_close').css({left: '-2000px', top: '-2000px'});
    $j('#buy_votes_close').show();
    $j('#modal_overlay').fadeOut('fast');

    form_reset();
  }
  
}

function thanks_cancel_btn() {
  $j("#buy_votes_thanks").fadeOut('fast');
  $j("#thanks_close").fadeOut('fast');
  $j('#modal_overlay').fadeOut('fast');
  
}

function show_buy_more_votes(){
  bv = get_position_buy_votes();
  $j("#buy_votes").hide();
  $j('#buy_votes_close').hide();
  $j("#buy_votes").css({left: bv.x, top: '65px'});
  $j('#buy_votes_close').css({left: bv.c_x, top: '50px'});
  
  $j('#modal_overlay').fadeIn('fast');
  $j("#buy_votes").fadeIn('fast');
  $j('#buy_votes_close').fadeIn('fast');
  vote_button_open = 1;
}

function show_thanks(){
  bv = get_position_thanks();
  $j("#buy_votes_thanks").hide();
  $j('#thanks_close').hide();
  $j("#buy_votes_thanks").css({left: bv.x, top: '65px'});
  $j('#thanks_close').css({left: bv.c_x, top: '50px'});
  
  $j('#modal_overlay').fadeIn('fast');
  $j("#buy_votes_thanks").fadeIn('fast');
  $j('#thanks_close').fadeIn('fast');
}

function resize_buy_more_votes(){
  bv = get_position_buy_votes();
  
  if ($j.browser.safari) {
    $j("#buy_votes").css({ left: bv.x });
    $j("#buy_votes_close").css({ left: bv.c_x });
  } else {
    $j("#buy_votes").animate({ left: bv.x });
    $j("#buy_votes_close").animate({ left: bv.c_x }); 
  }
}

function resize_thanks(){
  bv = get_position_thanks();
  
  $j("#buy_votes_thanks").animate({ left: bv.x });
  $j("#thanks_close").animate({ left: bv.c_x });
}

// LiveValidation 1.3 (standalone version)
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
// LiveValidation is licensed under the terms of the MIT License

/*********************************************** LiveValidation class ***********************************/

/**
 *	validates a form field in real-time based on validations you assign to it
 *	
 *	@var element {mixed} - either a dom element reference or the string id of the element to validate
 *	@var optionsObj {Object} - general options, see below for details
 *
 *	optionsObj properties:
 *							validMessage {String} 	- the message to show when the field passes validation
 *													  (DEFAULT: "Thankyou!")
 *							onValid {Function} 		- function to execute when field passes validation
 *													  (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); } )	
 *							onInvalid {Function} 	- function to execute when field fails validation
 *													  (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); })
 *							insertAfterWhatNode {Int} 	- position to insert default message
 *													  (DEFAULT: the field that is being validated)	
 *              onlyOnBlur {Boolean} - whether you want it to validate as you type or only on blur
 *                            (DEFAULT: false)
 *              wait {Integer} - the time you want it to pause from the last keystroke before it validates (ms)
 *                            (DEFAULT: 0)
 *              onlyOnSubmit {Boolean} - whether should be validated only when the form it belongs to is submitted
 *                            (DEFAULT: false)						
 */
var LiveValidation = function(element, optionsObj){
  	this.initialize(element, optionsObj);
};

LiveValidation.VERSION = '1.3 standalone';

/** element types constants ****/

LiveValidation.TEXTAREA = 1;
LiveValidation.TEXT 		= 2;
LiveValidation.PASSWORD = 3;
LiveValidation.CHECKBOX = 4;
LiveValidation.SELECT = 5;
LiveValidation.FILE = 6;

/****** Static methods *******/

/**
 *	pass an array of LiveValidation objects and it will validate all of them
 *	
 *	@var validations {Array} - an array of LiveValidation objects
 *	@return {Bool} - true if all passed validation, false if any fail						
 */
LiveValidation.massValidate = function(validations){
  var firstError = null;
  var returnValue = true;
	for(var i = 0, len = validations.length; i < len; ++i ){
		var valid = validations[i].validate();
                if (!valid) { 
                  if (!firstError) 
                    firstError = validations[i].element;
                }
		if(returnValue) { 
                  returnValue = valid;
                  }
	}
        if (firstError)
          firstError.focus();
	return returnValue;
}

/****** prototype ******/

LiveValidation.prototype = {

    validClass: 'LV_valid',
    invalidClass: 'LV_invalid',
    messageClass: 'LV_validation_message',
    validFieldClass: 'LV_valid_field',
    invalidFieldClass: 'LV_invalid_field',

    /**
     *	initialises all of the properties and events
     *
     * @var - Same as constructor above
     */
    initialize: function(element, optionsObj){
      var self = this;
      if(!element) throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");
    	this.element = element.nodeName ? element : document.getElementById(element);
    	if(!this.element) throw new Error("LiveValidation::initialize - No element with reference or id of '" + element + "' exists!");
      // default properties that could not be initialised above
    	this.validations = [];
      this.elementType = this.getElementType();
      this.form = this.element.form;
      // options
    	var options = optionsObj || {};
    	this.validMessage = options.validMessage || 'Thank you!';
    	var node = options.insertAfterWhatNode || this.element;
		this.insertAfterWhatNode = node.nodeType ? node : document.getElementById(node);
      this.onValid = options.onValid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); };
      this.onInvalid = options.onInvalid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); };	
    	this.onlyOnBlur =  options.onlyOnBlur || false;
    	this.wait = options.wait || 0;
      this.onlyOnSubmit = options.onlyOnSubmit || false;
      // add to form if it has been provided
      if(this.form){
        this.formObj = LiveValidationForm.getInstance(this.form);
        this.formObj.addField(this);
      }
      // events
      // collect old events
      this.oldOnFocus = this.element.onfocus || function(){};
      this.oldOnBlur = this.element.onblur || function(){};
      this.oldOnClick = this.element.onclick || function(){};
      this.oldOnChange = this.element.onchange || function(){};
      this.oldOnKeyup = this.element.onkeyup || function(){};
      this.element.onfocus = function(e){ self.doOnFocus(e); return self.oldOnFocus.call(this, e); }
      if(!this.onlyOnSubmit){
        switch(this.elementType){
          case LiveValidation.CHECKBOX:
            this.element.onclick = function(e){ self.validate(); return self.oldOnClick.call(this, e); }
          // let it run into the next to add a change event too
          case LiveValidation.SELECT:
          case LiveValidation.FILE:
            this.element.onchange = function(e){ self.validate(); return self.oldOnChange.call(this, e); }
            break;
          default:
            if(!this.onlyOnBlur) this.element.onkeyup = function(e){ self.deferValidation(); return self.oldOnKeyup.call(this, e); }
      	    this.element.onblur = function(e){ self.doOnBlur(e); return self.oldOnBlur.call(this, e); }
        }
      }
    },
	
	/**
     *	destroys the instance's events (restoring previous ones) and removes it from any LiveValidationForms
     */
    destroy: function(){
  	  if(this.formObj){
		// remove the field from the LiveValidationForm
		this.formObj.removeField(this);
		// destroy the LiveValidationForm if no LiveValidation fields left in it
		this.formObj.destroy();
	  }
      // remove events - set them back to the previous events
	  this.element.onfocus = this.oldOnFocus;
      if(!this.onlyOnSubmit){
        switch(this.elementType){
          case LiveValidation.CHECKBOX:
            this.element.onclick = this.oldOnClick;
          // let it run into the next to add a change event too
          case LiveValidation.SELECT:
          case LiveValidation.FILE:
            this.element.onchange = this.oldOnChange;
            break;
          default:
            if(!this.onlyOnBlur) this.element.onkeyup = this.oldOnKeyup;
      	    this.element.onblur = this.oldOnBlur;
        }
      }
      this.validations = [];
	  this.removeMessageAndFieldClass();
    },
    
    /**
     *	adds a validation to perform to a LiveValidation object
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validate.Presence )
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     * @return {Object} - the LiveValidation object itself so that calls can be chained
     */
    add: function(validationFunction, validationParamsObj){
      this.validations.push( {type: validationFunction, params: validationParamsObj || {} } );
      return this;
    },
    
	/**
     *	removes a validation from a LiveValidation object - must have exactly the same arguments as used to add it 
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validate.Presence )
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     * @return {Object} - the LiveValidation object itself so that calls can be chained
     */
    remove: function(validationFunction, validationParamsObj){
	  var found = false;
	  for( var i = 0, len = this.validations.length; i < len; i++ ){
	  		if( this.validations[i].type == validationFunction ){
				if (this.validations[i].params == validationParamsObj) {
					found = true;
					break;
				}
			}
	  }
      if(found) this.validations.splice(i,1);
      return this;
    },
    
	
    /**
     * makes the validation wait the alotted time from the last keystroke 
     */
    deferValidation: function(e){
      if(this.wait >= 300) this.removeMessageAndFieldClass();
    	var self = this;
      if(this.timeout) clearTimeout(self.timeout);
      this.timeout = setTimeout( function(){ self.validate() }, self.wait); 
    },
        
    /**
     * sets the focused flag to false when field loses focus 
     */
    doOnBlur: function(e){
      this.focused = false;
      this.validate(e);
    },
        
    /**
     * sets the focused flag to true when field gains focus 
     */
    doOnFocus: function(e){
      this.focused = true;
      this.removeMessageAndFieldClass();
    },
    
    /**
     *	gets the type of element, to check whether it is compatible
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validate.Presence )
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     */
    getElementType: function(){
      switch(true){
        case (this.element.nodeName.toUpperCase() == 'TEXTAREA'):
        return LiveValidation.TEXTAREA;
      case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'TEXT'):
        return LiveValidation.TEXT;
      case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'PASSWORD'):
        return LiveValidation.PASSWORD;
      case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'CHECKBOX'):
        return LiveValidation.CHECKBOX;
      case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'FILE'):
        return LiveValidation.FILE;
      case (this.element.nodeName.toUpperCase() == 'SELECT'):
        return LiveValidation.SELECT;
        case (this.element.nodeName.toUpperCase() == 'INPUT'):
        	throw new Error('LiveValidation::getElementType - Cannot use LiveValidation on an ' + this.element.type + ' input!');
        default:
        	throw new Error('LiveValidation::getElementType - Element must be an input, select, or textarea!');
      }
    },
    
    /**
     *	loops through all the validations added to the LiveValidation object and checks them one by one
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validate.Presence )
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     * @return {Boolean} - whether the all the validations passed or if one failed
     */
    doValidations: function(){
      	this.validationFailed = false;
      	for(var i = 0, len = this.validations.length; i < len; ++i){
    	 	var validation = this.validations[i];
    		switch(validation.type){
    		   	case Validate.Presence:
                case Validate.Confirmation:
                case Validate.Acceptance:
    		   		this.displayMessageWhenEmpty = true;
    		   		this.validationFailed = !this.validateElement(validation.type, validation.params); 
    				break;
    		   	default:
    		   		this.validationFailed = !this.validateElement(validation.type, validation.params);
    		   		break;
    		}
    		if(this.validationFailed) return false;	
    	}
    	this.message = this.validMessage;
    	return true;
    },
    
    /**
     *	performs validation on the element and handles any error (validation or otherwise) it throws up
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validate.Presence )
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     * @return {Boolean} - whether the validation has passed or failed
     */
    validateElement: function(validationFunction, validationParamsObj){
      	var value = (this.elementType == LiveValidation.SELECT) ? this.element.options[this.element.selectedIndex].value : this.element.value;     
        if(validationFunction == Validate.Acceptance){
    	    if(this.elementType != LiveValidation.CHECKBOX) throw new Error('LiveValidation::validateElement - Element to validate acceptance must be a checkbox!');
    		value = this.element.checked;
    	}
        var isValid = true;
      	try{    
    		validationFunction(value, validationParamsObj);
    	} catch(error) {
    	  	if(error instanceof Validate.Error){
    			if( value !== '' || (value === '' && this.displayMessageWhenEmpty) ){
    				this.validationFailed = true;
    				this.message = error.message;
    				isValid = false;
    			}
    		}else{
    		  	throw error;
    		}
    	}finally{
    	    return isValid;
        }
    },
    
    /**
     *	makes it do the all the validations and fires off the onValid or onInvalid callbacks
     *
     * @return {Boolean} - whether the all the validations passed or if one failed
     */
    validate: function(){
      if(!this.element.disabled){
		var isValid = this.doValidations();
		if(isValid){
			this.onValid();
			return true;
		}else {
			this.onInvalid();
			return false;
		}
	  }else{
      return true;
    }
    },
	
 /**
   *  enables the field
   *
   *  @return {LiveValidation} - the LiveValidation object for chaining
   */
  enable: function(){
  	this.element.disabled = false;
	return this;
  },
  
  /**
   *  disables the field and removes any message and styles associated with the field
   *
   *  @return {LiveValidation} - the LiveValidation object for chaining
   */
  disable: function(){
  	this.element.disabled = true;
	this.removeMessageAndFieldClass();
	return this;
  },
    
    /** Message insertion methods ****************************
     * 
     * These are only used in the onValid and onInvalid callback functions and so if you overide the default callbacks,
     * you must either impliment your own functions to do whatever you want, or call some of these from them if you 
     * want to keep some of the functionality
     */
    
    /**
     *	makes a span containg the passed or failed message
     *
     * @return {HTMLSpanObject} - a span element with the message in it
     */
    createMessageSpan: function(){
        var span = document.createElement('span');
        span.innerHTML = this.message;
    	//var textNode = document.createTextNode(this.message);
      	//span.appendChild(textNode);
        return span;
    },
    
    /**
     *	inserts the element containing the message in place of the element that already exists (if it does)
     *
     * @var elementToIsert {HTMLElementObject} - an element node to insert
     */
    insertMessage: function(elementToInsert){
      	this.removeMessage();
      	if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value == ''))
    	  || this.element.value != '' ){
            var className = this.validationFailed ? this.invalidClass : this.validClass;
    	  	elementToInsert.className += ' ' + this.messageClass + ' ' + className;
            if(this.insertAfterWhatNode.nextSibling){
    		  		this.insertAfterWhatNode.parentNode.insertBefore(elementToInsert, this.insertAfterWhatNode.nextSibling);
    		}else{
    			    this.insertAfterWhatNode.parentNode.appendChild(elementToInsert);
    	    }
    	}
    },
    
    
    /**
     *	changes the class of the field based on whether it is valid or not
     */
    addFieldClass: function(){
        this.removeFieldClass();
        if(!this.validationFailed){
            if(this.displayMessageWhenEmpty || this.element.value != ''){
                if(this.element.className.indexOf(this.validFieldClass) == -1) this.element.className += ' ' + this.validFieldClass;
            }
        }else{
            if(this.element.className.indexOf(this.invalidFieldClass) == -1) this.element.className += ' ' + this.invalidFieldClass;
        }
    },
    
    /**
     *	removes the message element if it exists, so that the new message will replace it
     */
    removeMessage: function(){
    	var nextEl;
    	var el = this.insertAfterWhatNode;
    	while(el.nextSibling){
    	    if(el.nextSibling.nodeType === 1){
    		  	nextEl = el.nextSibling;
    		  	break;
    		}
    		el = el.nextSibling;
    	}
      	if(nextEl && nextEl.className.indexOf(this.messageClass) != -1) this.insertAfterWhatNode.parentNode.removeChild(nextEl);
    },
    
    /**
     *	removes the class that has been applied to the field to indicte if valid or not
     */
    removeFieldClass: function(){
      if(this.element.className.indexOf(this.invalidFieldClass) != -1) this.element.className = this.element.className.split(this.invalidFieldClass).join('');
      if(this.element.className.indexOf(this.validFieldClass) != -1) this.element.className = this.element.className.split(this.validFieldClass).join(' ');
    },
        
    /**
     *	removes the message and the field class
     */
    removeMessageAndFieldClass: function(){
			/**
			 * this was commented out so messages won't disappear when a user clicks a submit button
			*/
      //this.removeMessage();
      this.removeFieldClass();
    }

} // end of LiveValidation class

/*************************************** LiveValidationForm class ****************************************/
/**
 * This class is used internally by LiveValidation class to associate a LiveValidation field with a form it is icontained in one
 * 
 * It will therefore not really ever be needed to be used directly by the developer, unless they want to associate a LiveValidation 
 * field with a form that it is not a child of
 */

/**
   *	handles validation of LiveValidation fields belonging to this form on its submittal
   *	
   *	@var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm
   */
var LiveValidationForm = function(element){
  this.initialize(element);
}

/**
 * namespace to hold instances
 */
LiveValidationForm.instances = {};

/**
   *	gets the instance of the LiveValidationForm if it has already been made or creates it if it doesnt exist
   *	
   *	@var element {HTMLFormElement} - a dom element reference to a form
   */
LiveValidationForm.getInstance = function(element){
  var rand = Math.random() * Math.random();
  if(!element.id) element.id = 'formId_' + rand.toString().replace(/\./, '') + new Date().valueOf();
  if(!LiveValidationForm.instances[element.id]) LiveValidationForm.instances[element.id] = new LiveValidationForm(element);
  return LiveValidationForm.instances[element.id];
}

LiveValidationForm.prototype = {
  
  /**
   *	constructor for LiveValidationForm - handles validation of LiveValidation fields belonging to this form on its submittal
   *	
   *	@var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm
   */
  initialize: function(element){
  	this.name = element.id;
    this.element = element;
    this.fields = [];
    // preserve the old onsubmit event
	this.oldOnSubmit = this.element.onsubmit || function(){};
    var self = this;
    this.element.onsubmit = function(e){
      return (LiveValidation.massValidate(self.fields)) ? self.oldOnSubmit.call(this, e || window.event) !== false : false;
    }
  },
  
  /**
   *	adds a LiveValidation field to the forms fields array
   *	
   *	@var element {LiveValidation} - a LiveValidation object
   */
  addField: function(newField){
    this.fields.push(newField);
  },
  
  /**
   *	removes a LiveValidation field from the forms fields array
   *	
   *	@var victim {LiveValidation} - a LiveValidation object
   */
  removeField: function(victim){
  	var victimless = [];
  	for( var i = 0, len = this.fields.length; i < len; i++){
		if(this.fields[i] !== victim) victimless.push(this.fields[i]);
	}
    this.fields = victimless;
  },
  
  /**
   *	destroy this instance and its events
   *
   * @var force {Boolean} - whether to force the detruction even if there are fields still associated
   */
  destroy: function(force){
  	// only destroy if has no fields and not being forced
  	if (this.fields.length != 0 && !force) return false;
	// remove events - set back to previous events
	this.element.onsubmit = this.oldOnSubmit;
	// remove from the instances namespace
	LiveValidationForm.instances[this.name] = null;
	return true;
  }
   
}// end of LiveValidationForm prototype

/*************************************** Validate class ****************************************/
/**
 * This class contains all the methods needed for doing the actual validation itself
 *
 * All methods are static so that they can be used outside the context of a form field
 * as they could be useful for validating stuff anywhere you want really
 *
 * All of them will return true if the validation is successful, but will raise a ValidationError if
 * they fail, so that this can be caught and the message explaining the error can be accessed ( as just 
 * returning false would leave you a bit in the dark as to why it failed )
 *
 * Can use validation methods alone and wrap in a try..catch statement yourself if you want to access the failure
 * message and handle the error, or use the Validate::now method if you just want true or false
 */

var Validate = {

    /**
     *	validates that the field has been filled in
     *
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation 
     *													  (DEFAULT: "Can't be empty!")
     */
    Presence: function(value, paramsObj){
      	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Can't be empty!";
    	if(value === '' || value === null || value === undefined){ 
    	  	Validate.fail(message);
    	}
    	return true;
    },
    
    /**
     *	validates that the value is numeric, does not fall within a given range of numbers
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							notANumberMessage {String} - the message to show when the validation fails when value is not a number
     *													  	  (DEFAULT: "Must be a number!")
     *							notAnIntegerMessage {String} - the message to show when the validation fails when value is not an integer
     *													  	  (DEFAULT: "Must be a number!")
     *							wrongNumberMessage {String} - the message to show when the validation fails when is param is used
     *													  	  (DEFAULT: "Must be {is}!")
     *							tooLowMessage {String} 		- the message to show when the validation fails when minimum param is used
     *													  	  (DEFAULT: "Must not be less than {minimum}!")
     *							tooHighMessage {String} 	- the message to show when the validation fails when maximum param is used
     *													  	  (DEFAULT: "Must not be more than {maximum}!")
     *							is {Int} 					- the length must be this long 
     *							minimum {Int} 				- the minimum length allowed
     *							maximum {Int} 				- the maximum length allowed
     *                         onlyInteger {Boolean} - if true will only allow integers to be valid
     *                                                             (DEFAULT: false)
     *
     *  NB. can be checked if it is within a range by specifying both a minimum and a maximum
     *  NB. will evaluate numbers represented in scientific form (ie 2e10) correctly as numbers				
     */
    Numericality: function(value, paramsObj){
        var suppliedValue = value;
        var value = Number(value);
    	var paramsObj = paramsObj || {};
        var minimum = ((paramsObj.minimum) || (paramsObj.minimum == 0)) ? paramsObj.minimum : null;;
        var maximum = ((paramsObj.maximum) || (paramsObj.maximum == 0)) ? paramsObj.maximum : null;
    	var is = ((paramsObj.is) || (paramsObj.is == 0)) ? paramsObj.is : null;
        var notANumberMessage = paramsObj.notANumberMessage || "Must be a number!";
        var notAnIntegerMessage = paramsObj.notAnIntegerMessage || "Must be an integer!";
    	var wrongNumberMessage = paramsObj.wrongNumberMessage || "Must be " + is + "!";
    	var tooLowMessage = paramsObj.tooLowMessage || "Must not be less than " + minimum + "!";
    	var tooHighMessage = paramsObj.tooHighMessage || "Must not be more than " + maximum + "!";
        if (!isFinite(value)) Validate.fail(notANumberMessage);
        if (paramsObj.onlyInteger && (/\.0+$|\.$/.test(String(suppliedValue))  || value != parseInt(value)) ) Validate.fail(notAnIntegerMessage);
    	switch(true){
    	  	case (is !== null):
    	  		if( value != Number(is) ) Validate.fail(wrongNumberMessage);
    			break;
    	  	case (minimum !== null && maximum !== null):
    	  		Validate.Numericality(value, {tooLowMessage: tooLowMessage, minimum: minimum});
    	  		Validate.Numericality(value, {tooHighMessage: tooHighMessage, maximum: maximum});
    	  		break;
    	  	case (minimum !== null):
    	  		if( value < Number(minimum) ) Validate.fail(tooLowMessage);
    			break;
    	  	case (maximum !== null):
    	  		if( value > Number(maximum) ) Validate.fail(tooHighMessage);
    			break;
    	}
    	return true;
    },
    
    /**
     *	validates against a RegExp pattern
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Not valid!")
     *							pattern {RegExp} 		- the regular expression pattern
     *													  (DEFAULT: /./)
     *             negate {Boolean} - if set to true, will validate true if the pattern is not matched
   *                           (DEFAULT: false)
     *
     *  NB. will return true for an empty string, to allow for non-required, empty fields to validate.
     *		If you do not want this to be the case then you must either add a LiveValidation.PRESENCE validation
     *		or build it into the regular expression pattern
     */
    Format: function(value, paramsObj){
      var value = String(value);
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Not valid!";
      var pattern = paramsObj.pattern || /./;
      var negate = paramsObj.negate || false;
      if(!negate && !pattern.test(value)) Validate.fail(message); // normal
      if(negate && pattern.test(value)) Validate.fail(message); // negated
    	return true;
    },
    
    /**
     *	validates that the field contains a valid email address
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Must be a number!" or "Must be an integer!")
     */
    Email: function(value, paramsObj){
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Must be a valid email address!";
    	Validate.Format(value, { failureMessage: message, pattern: /^\s*[a-z0-9._+-]+@([a-z0-9-]+\.)+[a-z]{2,6}\s*$|^Email Address$/i } );
    	return true;
    },

    UniqueEmail: function(value, paramsObj){
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || 'Darn, that email is already being used.' + 
                                                  ' If you&rsquo;ve signed up before, <a href="/">click here</a> to log in. ';
        if (value === '' || value === null || value === undefined) {
                Validate.fail(message);
        }
        $j.ajax({ 
                  async: false,
                  type: 'POST', 
                  url: '/user/join/validate_email',
                  data: { email: value }, 
                  success: function(data) {
                    if (data == 'success') {
                         return true;
                    } else {
                         Validate.fail(message);
                    } 
                  }
        });
        return true;
    },

    UniqueLogin: function(value, paramsObj) {
    	var paramsObj = paramsObj || {};
      var s_taken   = 'Darn, that user name is already being used by someone else.  Please choose a different user name. ';
    	var message = paramsObj.failureMessage || s_taken;

        if (value === '' || value === null || value === undefined) {
                Validate.fail(message);
        }
    	Validate.Format(value, { failureMessage: 'Please use only letters, numbers, and under_scores in your user name.', pattern: /^\s*[a-z][a-z0-9_]+\s*$/i } );
        $j.ajax({ 
                  async: false,
                  type: 'POST', 
                  url: '/user/join/validate_login',
                  data: { login: value }, 
                  success: function(data) {
                    if (data == 'success') {
                         return true;
                    } else {
                         Validate.fail(message);
                    } 
                  }
        });
        return true; 
    },

    PasswordFormat: function(value, paramsObj){
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Please limit your text to the following characters: a-z, 0-9, and ~`!@#$%&*()_-+={}|\":?></.,;'\][";
    	Validate.Format(value, { failureMessage: message, pattern: /[a-z0-9=`~!@#\$%^&*_+{}|":?><\/.,;'\\\-()\[\]]+/i } );
    	return true;
    },

    /**
     *	validates the length of the value
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							wrongLengthMessage {String} - the message to show when the fails when is param is used
     *													  	  (DEFAULT: "Must be {is} characters long!")
     *							tooShortMessage {String} 	- the message to show when the fails when minimum param is used
     *													  	  (DEFAULT: "Must not be less than {minimum} characters long!")
     *							tooLongMessage {String} 	- the message to show when the fails when maximum param is used
     *													  	  (DEFAULT: "Must not be more than {maximum} characters long!")
     *							is {Int} 					- the length must be this long 
     *							minimum {Int} 				- the minimum length allowed
     *							maximum {Int} 				- the maximum length allowed
     *
     *  NB. can be checked if it is within a range by specifying both a minimum and a maximum				
     */
    Length: function(value, paramsObj){
    	var value = String(value);
    	var paramsObj = paramsObj || {};
        var minimum = ((paramsObj.minimum) || (paramsObj.minimum == 0)) ? paramsObj.minimum : null;
    	var maximum = ((paramsObj.maximum) || (paramsObj.maximum == 0)) ? paramsObj.maximum : null;
    	var is = ((paramsObj.is) || (paramsObj.is == 0)) ? paramsObj.is : null;
        var wrongLengthMessage = paramsObj.wrongLengthMessage || "Must be " + is + " characters long!";
    	var tooShortMessage = paramsObj.tooShortMessage || "Must not be fewer than " + minimum + " characters long!";
    	var tooLongMessage = paramsObj.tooLongMessage || "Must not be more than " + maximum + " characters long!";
    	switch(true){
    	  	case (is !== null):
    	  		if( value.length != Number(is) ) Validate.fail(wrongLengthMessage);
    			break;
    	  	case (minimum !== null && maximum !== null):
    	  		Validate.Length(value, {tooShortMessage: tooShortMessage, minimum: minimum});
    	  		Validate.Length(value, {tooLongMessage: tooLongMessage, maximum: maximum});
    	  		break;
    	  	case (minimum !== null):
    	  		if( value.length < Number(minimum) ) Validate.fail(tooShortMessage);
    			break;
    	  	case (maximum !== null):
    	  		if( value.length > Number(maximum) ) Validate.fail(tooLongMessage);
    			break;
    		default:
    			throw new Error("Validate::Length - Length(s) to validate against must be provided!");
    	}
    	return true;
    },
    
    /**
     *	validates that the value falls within a given set of values
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Must be included in the list!")
     *							within {Array} 			- an array of values that the value should fall in 
     *													  (DEFAULT: [])	
     *							allowNull {Bool} 		- if true, and a null value is passed in, validates as true
     *													  (DEFAULT: false)
     *             partialMatch {Bool} 	- if true, will not only validate against the whole value to check but also if it is a substring of the value 
     *													  (DEFAULT: false)
     *             caseSensitive {Bool} - if false will compare strings case insensitively
     *                          (DEFAULT: true)
     *             negate {Bool} 		- if true, will validate that the value is not within the given set of values
     *													  (DEFAULT: false)			
     */
    Inclusion: function(value, paramsObj){
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Must be included in the list!";
      var caseSensitive = (paramsObj.caseSensitive === false) ? false : true;
    	if(paramsObj.allowNull && value == null) return true;
      if(!paramsObj.allowNull && value == null) Validate.fail(message);
    	var within = paramsObj.within || [];
      //if case insensitive, make all strings in the array lowercase, and the value too
      if(!caseSensitive){ 
        var lowerWithin = [];
        for(var j = 0, length = within.length; j < length; ++j){
        	var item = within[j];
          if(typeof item == 'string') item = item.toLowerCase();
          lowerWithin.push(item);
        }
        within = lowerWithin;
        if(typeof value == 'string') value = value.toLowerCase();
      }
    	var found = false;
    	for(var i = 0, length = within.length; i < length; ++i){
    	  if(within[i] == value) found = true;
        if(paramsObj.partialMatch){ 
          if(value.indexOf(within[i]) != -1) found = true;
        }
    	}
    	if( (!paramsObj.negate && !found) || (paramsObj.negate && found) ) Validate.fail(message);
    	return true;
    },
    
    /**
     *	validates that the value does not fall within a given set of values
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Must not be included in the list!")
     *							within {Array} 			- an array of values that the value should not fall in 
     *													  (DEFAULT: [])
     *							allowNull {Bool} 		- if true, and a null value is passed in, validates as true
     *													  (DEFAULT: false)
     *             partialMatch {Bool} 	- if true, will not only validate against the whole value to check but also if it is a substring of the value 
     *													  (DEFAULT: false)
     *             caseSensitive {Bool} - if false will compare strings case insensitively
     *                          (DEFAULT: true)			
     */
    Exclusion: function(value, paramsObj){
      var paramsObj = paramsObj || {};
    	paramsObj.failureMessage = paramsObj.failureMessage || "Must not be included in the list!";
      paramsObj.negate = true;
    	Validate.Inclusion(value, paramsObj);
      return true;
    },
    
    /**
     *	validates that the value matches that in another field
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Does not match!")
     *							match {String} 			- id of the field that this one should match						
     */
    Confirmation: function(value, paramsObj){
      	if(!paramsObj.match) throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");
    	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Does not match!";
    	var match = paramsObj.match.nodeName ? paramsObj.match : document.getElementById(paramsObj.match);
    	if(!match) throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '" + paramsObj.match + "'!");
    	if(value != match.value){ 
    	  	Validate.fail(message);
    	}
    	return true;
    },
    
    /**
     *	validates that the value is true (for use primarily in detemining if a checkbox has been checked)
     *	
     *	@var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox)
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation 
     *													  (DEFAULT: "Must be accepted!")
     */
    Acceptance: function(value, paramsObj){
      	var paramsObj = paramsObj || {};
    	var message = paramsObj.failureMessage || "Must be accepted!";
    	if(!value){ 
    	  	Validate.fail(message);
    	}
    	return true;
    },
    
	 /**
     *	validates against a custom function that returns true or false (or throws a Validate.Error) when passed the value
     *	
     *	@var value {mixed} - value to be checked
     *	@var paramsObj {Object} - parameters for this particular validation, see below for details
     *
     *	paramsObj properties:
     *							failureMessage {String} - the message to show when the field fails validation
     *													  (DEFAULT: "Not valid!")
     *							against {Function} 			- a function that will take the value and object of arguments and return true or false 
     *													  (DEFAULT: function(){ return true; })
     *							args {Object} 		- an object of named arguments that will be passed to the custom function so are accessible through this object within it 
     *													  (DEFAULT: {})
     */
	Custom: function(value, paramsObj){
		var paramsObj = paramsObj || {};
		var against = paramsObj.against || function(){ return true; };
		var args = paramsObj.args || {};
		var message = paramsObj.failureMessage || "Not valid!";
	    if(!against(value, args)) Validate.fail(message);
	    return true;
	  },
	
    /**
     *	validates whatever it is you pass in, and handles the validation error for you so it gives a nice true or false reply
     *
     *	@var validationFunction {Function} - validation function to be used (ie Validation.validatePresence )
     *	@var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox)
     *	@var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
     */
    now: function(validationFunction, value, validationParamsObj){
      	if(!validationFunction) throw new Error("Validate::now - Validation function must be provided!");
    	var isValid = true;
        try{    
    		validationFunction(value, validationParamsObj || {});
    	} catch(error) {
    		if(error instanceof Validate.Error){
    			isValid =  false;
    		}else{
    		 	throw error;
    		}
    	}finally{ 
            return isValid 
        }
    },
    
    /**
     * shortcut for failing throwing a validation error
     *
     *	@var errorMessage {String} - message to display
     */
    fail: function(errorMessage){
            throw new Validate.Error(errorMessage);
    },
    
    Error: function(errorMessage){
    	this.message = errorMessage;
    	this.name = 'ValidationError';
    }
}


// $Id: track.js 16977 2009-10-26 20:48:50Z wil $

/* This variable is the EVENT TRACKING STRING MAPPER.
 * It takes an event such as "PROFILE EDIT" and returns
 * the url that should be tracked as a result.. in this case
 * '/profile/edit.'
*/

var event_ts_map = {
  PROFILE_EDIT:                 '/profile/edit',
  ICON_UPLOAD:                  '/profile/icon/edit',
  PHOTO_UPLOAD:                 '/profile/photo/edit',
  HOME:                         '/user/home',
  INVITE_CLICKED:               '/user/invites/clicked',
  INVITE_CLICKED_WL:            '/user/invites/clicked/from_waiting_list',
  INVITE_FULFILLED:             '/user/invites/fulfilled',
  INVITE_SENT:                  '/user/invites/sent/',
  INVITE_SENT_WL:               '/user/invites/sent/waiting_list/',
  LOGOUT:                       '/auth/logout',
  LOGIN:                        '/auth/login/success',
  PSET_UPLOAD_START:            '/photoset/upload/started',
  PSET_UPLOAD_COMP:             '/photoset/upload/completed',
  PSET_UPLOAD_VER:              '/photoset/upload/verified',
  MESSAGE_TO_USER:              '/messages/sent/to_user',
  MESSAGE_TO_PHOT:              '/messages/sent/to_photographer',
  MESSAGE_TO_MODEL:             '/messages/sent/to_model',
  MESSAGE_TO_FANS:              '/messages/sent/to_fans',
  MESSAGE_FROM_USER:            '/messages/view/from_user',
  MESSAGE_FROM_PHOT:            '/messages/view/from_photographer',
  MESSAGE_FROM_MODEL:           '/messages/view/from_model',
  FAN_MESSAGE_MODEL:            '/messages/view/fan_message_from_model',
  FAN_MESSAGE_PHOT:             '/messages/view/fan_message_from_photographer',
  FRIEND_FROM_USER:             '/friend/from_user',
  FRIEND_FROM_MODEL:            '/friend/from_model',
  FRIEND_FROM_PHOT:             '/friend/from_photographer',
  FRIEND_TO_USER:               '/friend/to_user',
  FRIEND_TO_MODEL:              '/friend/to_model',
  FRIEND_TO_PHOT:               '/friend/to_photographer',
  WWW_SIGNUP:                   '/www/signup',
  PURCH_VOTES_CLICK_PAYPAL:     '/purchases/buy_votes/clicked/paypal',
  PURCH_VOTES_CLICK_CC:         '/purchases/buy_votes/clicked/CC',
  PURCH_VOTES_COMPLETE_PAYPAL:  '/purchases/buy_votes/completed/paypal',
  PURCH_VOTES_COMPLETE_CC:      '/purchases/buy_votes/completed/CC'
};

/* This method will track a page view using google analytics.
 * It expects to receive an EVENT Constant - E.g. 'LOGIN'
 * It will use the event constant as input to the event_ts_map hash
 * and then track a page view to the resulting tracking string.
 * If there is a value passed into x_var then that value is appended at the end
 * of the track_string - e.g. track_page_view('INVITE_SENT', '4');
 * would result in tracking '/user/invites/sent/4' 
 * */
function track_page_view( event_const, n_var ) {
  // uses GA 2.0; if it's not included on this page, then don't do anything
  if (!window._gat) { return false; }
  track_string = event_ts_map[event_const];
  //track_string = (n_var === null) ? track_string : track_string + n_var;
  
  pageTracker._trackPageview( track_string );
}

/* This method simply tracks the string that is passed in.
 * It handles weird cases where the url is custom built such as
 * track_page_view_custom( '/' + page.artist_role + 's/' + page.artist + '/photosets/' + page.artist_pnum + '/vote'); 
 * */
function track_page_view_custom( custom_url ) {
  if (!window._gat) { return false; }
  pageTracker._trackPageview( custom_url );
}

function mail_link( e, s ) {
  track_page_view_custom( "/email/" + s );
  window.location = "mailto:" + e;
}