//Add, or add to, an event
function addEvent(obj, evType, fn){ 
	if (obj.addEventListener){ 
		obj.addEventListener(evType, fn, false); 
		return true; 
	} else if (obj.attachEvent){ 
		var r = obj.attachEvent("on"+evType, fn); 
		return r; 
	} else { 
		return false; 
	} 
}

//Return a human-friendly version of the an epoch time as number of days, minutes, seconds etc from the current time.
//Eg: "1 day, 2 hours".
//targettime is the EPOCH seconds of the target time (a time in the past) to measure.
//curtime is the current time in EPOCH seconds.
//NOTE: See humanDate() function in core.php. These two functions should be maintained to output ELAPSED times in identical format.
function humanDateElapsed(targettime, curtime) {

	original = targettime;
	
	// array of time period chunks
	
	var a = new Array(60 * 60 * 24 * 365 , 'year');
	var b = new Array(60 * 60 * 24 * 30 , 'month');
	var c = new Array(60 * 60 * 24 * 7, 'week');
	var d = new Array(60 * 60 * 24 , 'day');
	var e = new Array(60 * 60 , 'hr');
	var f = Array(60 , 'min');
	
	var chunks = new Array(a,b,c,d,e,f);
	
	today = curtime;
	since = today - original;

	//less than a minute ago?
	if (since < 60) {
		return "a moment";
	}
	
	// $j saves performing the count function each time around the loop
	for (i = 0, j = chunks.length; i < j; i++) {
		
		seconds = chunks[i][0];
		name1 = chunks[i][1];
		
		// finding the biggest chunk (if the chunk fits, break)
		if ((count = Math.floor(since / seconds)) != 0) {
			break;
		}
	}
	
	printx = (count == 1) ? '1 '+name1 : count+' '+name1+'s';
	
	if (i + 1 < j) {
		// now getting the second item
		seconds2 = chunks[i + 1][0];
		name2 = chunks[i + 1][1];
		
		// add second item if it's greater than 0
		if ((count2 = Math.floor((since - (seconds * count)) / seconds2)) != 0) {
		printx += (count2 == 1) ? ', 1 '+name2 : ', '+count2+' '+name2+'s';
		}
	}
	
	return printx;
}

//TEMPORARILY DISABLED - see bugs to do with IE7 and clicking the create submission etc ribbons.
//addEvent(window, 'load', init_createribbons);

//Check if the button has been pressed more than once.
//To add this check to a button, put "return multisubmitcheck(this);" in its onClick event.
trackmultisubmit = 0;
function dosomething(e) {
	if (trackmultisubmit>1){ 
		Event.stop(e);
		alert('You have pressed a submit more than once.\n\nThe server is already processing your request, so you may just need to wait longer.\n\nIf you really want to submit the form again anyway, press the button once more.\n\nThis will interrupt any current uploads. It may result in a double post.');
		trackmultisubmit = 0;
	}
}
function multisubmitcheck(obj) {
	trackmultisubmit = trackmultisubmit + 1;
	if (!obj.observerattached) {
		Event.observe(obj, 'click', dosomething, false);
		obj.observerattached = true;
	}

	return true; 

}

//put together the scene on page load
addEvent(window, 'load', init_celestialmachinations);

//run celestialmachinations once, after that it'll take care of itself
function init_celestialmachinations() {
	//Run Celestial machinations every minute
	setInterval("celestialmachinations();", 60000);
}

function celestialmachinations() {

	//If no user location set, then skip celestialmachinations.
	if (nolocation || stopmachinations) return true;
	
	//what period of day is it for the user
	newtime = false;
	if (skoo > dawn_start) {
		newtime = 'dawn';
	} 
	if (skoo > daytime_start) {
		newtime = 'daytime';
	} 
	if (skoo > dusk_start) {
		newtime = 'dusk';
	} 
	if (skoo > night_start) {
		newtime = 'night';
	} 
	if (!newtime) {
		newtime = 'night';
	}

	if (newtime != global_timeofday) {
		global_timeofday = newtime;
		original_timeofday = global_timeofday;
		//Load new background, but instruct serverside script via ajax NOT to save the new bg choice (leaves choice "dynamic" in user prefs then).
		getBackground(clientx, clienty, newtime, global_bgtheme, devicePixelRatio, 'no');
	}

	//if timer has reached 24 hours, reset
	if (skoo > (24 * 60 * 60)) {
		skoo = 0;

	//increment the timer
	} else {
		skoo += 60;
	}
	
}
		
//HTTPS check and warning.
if (window.location.protocol != 'https:') {
		alert("WARNING: This page is NOT encrypted or secure.\n\nSomething has removed the encryption layer for this connection.\n\nIf you are using a proxy or other redirector/proxy service, then the service is set up to potentially harvest your personal data.\n\nIt is also possible that someone has compromised the local network to make this happen.\n\nYour account may be compromised.\n\nDO NOT LOG IN or use Inkbunny from your current location\n\nand please notify us at admin@inkbunny.net immediately.");
}

//Basic framebuster.
if ( self == top ) {
} else {
	self.location = 'http://google.com/'; 
	throw new Error('Exiting Javascript.');
}

//variables to tell the places of the create submission popout thingies
var createsubmissionribbon_left_start = 14;
var createsubmissionribbon_bottom_start = 2;

var createsubmissionribbon_left_finish = 15;
var createsubmissionribbon_bottom_finish = 80;


var createstreamribbon_left_start = 24;
var createstreamribbon_bottom_start = 0;

var createstreamribbon_left_finish = 0;
var createstreamribbon_bottom_finish = 42;


var createjournalribbon_left_start = 24;
var createjournalribbon_bottom_start = 2;

var createjournalribbon_left_finish = 48;
var createjournalribbon_bottom_finish = 44;

var createribbons_sequence = 0;

var createribbons_sequence_tracker;

function init_createribbons() {
	if (!$('createribbons')) return false;

	$('createribbons').onclick = function () {
		createribbons_sequencer(1);

		document.onclick = function () {
			if (createribbons_sequence == 10) {
				createribbons_sequencer(-1);
			}
		}
	}
}

function createribbons_sequencer(sequence_addition) {
	clearTimeout(createribbons_sequence_tracker);

	if (createribbons_sequence == 0 && sequence_addition > 0) {
		$('createribbons').style.height = '108px';

		if ($('createsubmissionribbon')) $('createsubmissionribbon').setStyle({visibility: 'visible'});
		if ($('createstreamribbon')) $('createstreamribbon').setStyle({visibility: 'visible'});
		if ($('createjournalribbon')) $('createjournalribbon').setStyle({visibility: 'visible'});

		if ($('createsubmissionribbon')) $('createsubmissionribbon').onclick = function () { return true; }
		if ($('createstreamribbon')) $('createstreamribbon').onclick = function () { return true; }
		if ($('createjournalribbon')) $('createjournalribbon').onclick = function () { return true; }

	}

	createsubmissionribbon_left_step = (createsubmissionribbon_left_finish - createsubmissionribbon_left_start) / 10;
	createsubmissionribbon_left = createsubmissionribbon_left_start + createsubmissionribbon_left_step * createribbons_sequence;

	createsubmissionribbon_bottom_step = (createsubmissionribbon_bottom_finish - createsubmissionribbon_bottom_start) / 10;
	createsubmissionribbon_bottom = createsubmissionribbon_bottom_start + createsubmissionribbon_bottom_step * createribbons_sequence;


	createstreamribbon_left_step = (createstreamribbon_left_finish - createstreamribbon_left_start) / 10;
	createstreamribbon_left = createstreamribbon_left_start + createstreamribbon_left_step * createribbons_sequence;

	createstreamribbon_bottom_step = (createstreamribbon_bottom_finish - createstreamribbon_bottom_start) / 10;
	createstreamribbon_bottom = createstreamribbon_bottom_start + createstreamribbon_bottom_step * createribbons_sequence;


	createjournalribbon_left_step = (createjournalribbon_left_finish - createjournalribbon_left_start) / 10;
	createjournalribbon_left = createjournalribbon_left_start + createjournalribbon_left_step * createribbons_sequence;

	createjournalribbon_bottom_step = (createjournalribbon_bottom_finish - createjournalribbon_bottom_start) / 10;
	createjournalribbon_bottom = createjournalribbon_bottom_start + createjournalribbon_bottom_step * createribbons_sequence;


	if ($('createsubmissionribbon')) $('createsubmissionribbon').style.left = createsubmissionribbon_left+'px';
	if ($('createsubmissionribbon')) $('createsubmissionribbon').style.bottom = createsubmissionribbon_bottom+'px';

	if ($('createsubmissionribbon')) $('createsubmissionribbon').setOpacity(createribbons_sequence / 10);

	if ($('createstreamribbon')) $('createstreamribbon').style.left = createstreamribbon_left+'px';
	if ($('createstreamribbon')) $('createstreamribbon').style.bottom = createstreamribbon_bottom+'px';

	if ($('createstreamribbon')) $('createstreamribbon').setOpacity(createribbons_sequence / 10);

	if ($('createjournalribbon')) $('createjournalribbon').style.left = createjournalribbon_left+'px';
	if ($('createjournalribbon')) $('createjournalribbon').style.bottom = createjournalribbon_bottom+'px';

	if ($('createjournalribbon')) $('createjournalribbon').setOpacity(createribbons_sequence / 10);

	if (createribbons_sequence == 0 && sequence_addition < 0) {
		$('createribbons').style.height = '41px';

		if ($('createsubmissionribbon')) $('createsubmissionribbon').setStyle({visibility: 'hidden'});
		if ($('createstreamribbon')) $('createstreamribbon').setStyle({visibility: 'hidden'});
		if ($('createjournalribbon')) $('createjournalribbon').setStyle({visibility: 'hidden'});

		if ($('createsubmissionribbon')) $('createsubmissionribbon').onclick = function () { return false; }
		if ($('createstreamribbon')) $('createstreamribbon').onclick = function () { return false; }
		if ($('createjournalribbon')) $('createjournalribbon').onclick = function () { return false; }

	}

	if ((createribbons_sequence < 10 || sequence_addition < 0) && (createribbons_sequence > 0 || sequence_addition > 0)) {
		createribbons_sequence += sequence_addition;

		createribbons_sequence_tracker = setTimeout("createribbons_sequencer("+sequence_addition+");", 33);
	}
}

//Generate a random alphanumeric string of length string_length, including characters in the ranges 0-9,A-Z,a-z.
function randomString(string_length) {
		
	var newDate = new Date;
	
	var seed = newDate.getTime();

	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	var randomstring = '';
	for (var i=0; i<string_length; i++) {
		var rnum = Math.floor(Math.random(seed) * chars.length);
		randomstring += chars.substring(rnum,rnum+1);
	}
	return randomstring;
}

//rot 13 encryption used by email obfuctator
function rot13init() {
	var map = new Array();
	var s = "abcdefghijklmnopqrstuvwxyz";
	for (var i = 0 ; i < s.length ; i++)
		map[s.charAt(i)] = s.charAt((i+13)%26);
	for (var i = 0 ; i < s.length ; i++)
		map[s.charAt(i).toUpperCase()] = s.charAt((i+13)%26).toUpperCase();
	return map;
}

//rot 13 encryption used by email obfuctator
function str_rot13(a,map) {
	var s = "";
	for (var i = 0 ; i < a.length ; i++) {
		var b = a.charAt(i);
		s += (b>='A' && b<='Z' || b>='a' && b<='z' ? map[b] : b);
	}
	return s;
}

function submissionsviewall_wide_init() {
	submissionsviewall_wide_resize();
	addEvent(window, 'resize', submissionsviewall_wide_resize);
}

//Resize all "wide screen" elements that need to fit the screen width and allow correct internal magins/padding to stack thumbnails neatly centered.
//NOTE: If this function is updated you must also update its sister function in PHP by the same name.
function submissionsviewall_wide_resize() {

	//get the width of the viewport
	var viewport_width = document.viewport.getWidth();

	//find all the expanable elephants
	expandables = $$("div.elephant_expandable");

	//if the page is only wide enough to support the normal width
	if (viewport_width <= 960 + 100 + 16) {
		//loop through all expandable elephants
		for (i in expandables) {
			//ignore things that aren't elephants
			if (expandables[i] && expandables[i].style) {
				//use the normal width
				expandables[i].style.width = 960 + 'px';
			}
		}

		return;
	}

	//NOTE: These values need to be updated if the page margin or padding CSS values are changed.
	var page_margin = 12;
	
	//Check if the user is viewing the gallery in large thumbnail mode or not.
	//NOTE: These values need to be updated if the thumbnail size and margin CSS values are changed.
	if (thumb_size=='t') {
		if (submissionsviewall_wide == 't') {
			if (viewport_width < 1077) {
				var thumb_margin = 1;
			}
			else if (viewport_width < 1425) {
				var thumb_margin = 8;
			}
			else {
				var thumb_margin = 15;
			}
		} else {
			var thumb_margin = 1;
		}
		var thumb_width = 310;
	} else if (thumb_size=='') {
		var thumb_margin = 5;
		var thumb_width = 220;
	} else {
		var thumb_margin = 8;
		var thumb_width = 140;
	}
	viewport_width -= (page_margin*2) + (thumb_margin*2);

	//how many thumbs could we put in the elephant for this page width?
	var thumbs_across = Math.floor(viewport_width / (thumb_width + (thumb_margin*2)));

	//work out the width of the page with that many thumbs, and the correct margins
	var elephant_inner_width = (thumbs_across * (thumb_width + (thumb_margin*2))) + (page_margin*2);

	if (elephant_inner_width < 960) elephant_inner_width = 960;
	
	//loop through all expandable elephants
	for (i in expandables) {
		//ignore things that aren't elephants
		if (expandables[i] && expandables[i].style) {
			//set width
			expandables[i].style.width = elephant_inner_width + 'px';
		}
	}
	
}

//SITE NOTICE BOX

	var timervar; //Countdown timer interval object.
	var blinkvar; //Countdown Blink timer interval object.
	var blinking = false; //Is the timer already blinking?
	var offlineblinkvar; //Offline header blink interval object;
	var offlineblink = false; //Is the offline header blinking?
	
	//Flash the countdown timer.
	//Set to 0 to turn off flashing.
	//Leave var rate blank for default speed or set to integer for millisecond flash rate.
	function site_notice_timer_blink(rate) {
		blinking = true;
		clearTimeout(blinkvar);
		//Rate is blink rate in milliseconds.
		if (rate==0) {
			$('site_notice_countdown_timer').removeClassName('site_notice_countdown_timer_flash');
			blinking = false;
			return true;
		}
		if (!rate) rate = 500;
		blinkvar = setTimeout("$('site_notice_countdown_timer').toggleClassName('site_notice_countdown_timer_flash');site_notice_timer_blink();",rate);
	}

	//Flash the OFFLINE notice heading.
	//Set to 0 to turn off flashing.
	//Leave var rate blank for default speed or set to integer for millisecond flash rate.
	function site_notice_offline_heading_blink(rate) {
		offlineblink = true;
		clearTimeout(offlineblinkvar);
		//Rate is blink rate in milliseconds.
		if (rate==0) {
			$('site_notice_offline_heading').removeClassName('site_notice_offline_heading_flash');
			offlineblink = false;
			return true;
		}
		if (!rate) rate = 500;
		offlineblinkvar = setTimeout("$('site_notice_offline_heading').toggleClassName('site_notice_offline_heading_flash');site_notice_offline_heading_blink();",rate);
	}
	
	//Show site OFFLINE notice (inside site notice box).
	function site_notice_show_offline() {
		site_notice_offline_heading_blink();
		$('site_notice_offline').addClassName('site_notice_offline_active');
	}
	
	//Hide site OFFLINE notice (inside site notice box).
	function site_notice_hide_offline() {
		site_notice_offline_heading_blink(0);
		$('site_notice_offline').removeClassName('site_notice_offline_active');
	}

	//Show site notice box.
	//Set slide true to show box with slide/fade animation.
	function site_notice_show(slide) {
		if (slide==true) {
			$('site_notice').addClassName('site_notice_active');
			Effect.SlideDown('site_notice', { duration: 1 });
			Effect.Appear('site_notice', { duration: 1 });
		} else {
			$('site_notice').addClassName('site_notice_active');
			$('site_notice').show();
		}	
		
	}

	//Hide site notice box.
	function site_notice_hide() {
		site_notice_timer_blink(0);
		Effect.SlideUp('site_notice', { duration: 1 });
		Effect.Fade('site_notice', { duration: 1 });
		site_notice_timer_blink(0);
		$('site_notice').removeClassName('site_notice_active');
	}

	//Return a human-friendly "stopwatch" string (HH:MM:SS) in hours, minutes, seconds of a target time from the current time.
	//Eg: "01:22:23".
	//targettime is the EPOCH seconds of the target time (a time in the future) to measure.
	//curtime is the current time in EPOCH seconds.
	function countdownElapsed(curtime, targettime) {

		remaining_seconds = targettime - curtime;
		
		if (remaining_seconds <= 0) return '00:00:00';
		
		hours = Math.floor(remaining_seconds / (60*60));
		hours_padded = String("0" + hours).slice(-2);
		
		remaining_seconds = remaining_seconds - (hours*60*60);
		
		minutes = Math.floor(remaining_seconds / (60));
		minutes_padded = String("0" + minutes).slice(-2);
		
		remaining_seconds = remaining_seconds - (minutes*60);
		
		seconds = remaining_seconds;
		seconds_padded = String("0" + seconds).slice(-2);
		
		return hours_padded + ':' + minutes_padded + ':' + seconds_padded;
		
	}

	function site_notice_setTimer() {
		//Get current epoch time.
		ko = new Date();
		ji = Math.floor(ko.getTime() / 1000);
		
		//Set the remainging time on the timer display.
		$('site_notice_countdown_timer').innerHTML = countdownElapsed(ji, $('site_notice_countdown_targettime_epoch').innerHTML*1);
		
		//If time is less than 5 minutes, start flashing the timer (but only if it's not already flashing).
		if (($('site_notice_countdown_targettime_epoch').innerHTML*1) - ji < 300) {
			if (!blinking) site_notice_timer_blink();
		} else {
			if (blinking) site_notice_timer_blink(0);
		}
	}

	function site_notice_startTimer() {
		$('site_notice_countdown').addClassName('site_notice_countdown_on');
		site_notice_setTimer();
		timervar = setInterval(site_notice_setTimer,1000);
	}
	
	function site_notice_hidemessage() {
		site_notice_hide();
		if (supports_storage) localStorage["site_status_dismiss"] = "yes";
		new Ajax.Request('/site_notice_hide_process.php',
		{
			parameters: {  },
			method:'get',
			requestHeaders: {Accept: 'application/json'},
			onSuccess: function(transport){
				var response = transport.responseText || "{'error':'no response'}";
				var jsondata = response.evalJSON(true);
					
			},
			onFailure: function() { 
				
			}
		});
	}
	
	//Get a cookie value named c_name.
	function getCustomCookie(c_name) {
		if (document.cookie.length>0)
		{
			c_start=document.cookie.indexOf(c_name + "=");
			if (c_start!=-1)
			{
				c_start=c_start + c_name.length+1;
				c_end=document.cookie.indexOf(";",c_start);
				if (c_end==-1) c_end=document.cookie.length;
				return unescape(document.cookie.substring(c_start,c_end));
			}
		}
		return "";
	}

	//Set a cookie value named cookieName with value cookieValue and store for up to nDays days.
	function setCustomCookie(cookieName,cookieValue,nDays) {
		var today = new Date();
		var expire = new Date();
		if (nDays==null || nDays==0) nDays=1;
		expire.setTime(today.getTime() + 3600000*24*nDays);
		document.cookie = cookieName+"="+escape(cookieValue) + ";path=/;secure;expires="+expire.toGMTString();
	}
	
	//Destory a cookie named cookieName.
	function deleteCustomCookie(cookieName)
	{
		document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
	}
	
	function site_notice_checkmessage(interval) {
		
		//Skip this process if HTML5 storage is not supported.
		if (!supports_storage) return true;
		
		//Get last time query sent to server.
		lasttime = localStorage["site_status_lastpoll"] || 0;
		
		//Get current milliseconds since epoch.
		var newDate = new Date;
		var curtime = newDate.getTime();
		
		//Time sanity check (in case user changed system clock or other crazyness.
		if (lasttime > curtime) {
			//Last poll time is in the future! This is impossible. Set it to current time.
			lasttime = curtime;
		}
		
		//Has more than the allowed timeout passed or is this the first time we have checked?
		if (lasttime=='' || curtime - lasttime >= interval) {
			
			//Store the current time as the last poll time.
			localStorage["site_status_lastpoll"] = curtime;
			
			//If so, poll server for new cookie value and store them, using the current time to bust any inadvertent caches.
			new Ajax.Request('/check_site_status_process2.php?'+curtime,
			{
				parameters: { },
				method:'get',
				requestHeaders: {Accept: 'application/json'},
				onSuccess: function(transport){
					var response = transport.responseText || "{'error':'no response'}";
					var jsondata = response.evalJSON(true);
					
					//Store site notice data.

					localStorage["site_status_maintmode"] = jsondata.maintmode || 'no';
					localStorage["site_status_notice_on"] = jsondata.notice_on || 'no';
					if (jsondata.notice_on && jsondata.notice_on=='yes') {
						localStorage["site_status_dismiss"] = 'no';
					}
					localStorage["site_status_time_on'"] = jsondata.time_on || 'no';
					localStorage["site_status_notice_text_bbcode_parsed"] = jsondata.notice_text_bbcode_parsed || false;
					localStorage["site_status_event_datetime_system"] = jsondata.event_datetime_system || false;
					localStorage["site_status_event_datetime_epoch"] = jsondata.event_datetime_epoch || false;

					//Process site notice data.
					site_notice_updatedetails();
					
					
					//Process incoming user notices data.
					dataArray = new Array();
				
					dataArray["unread_notices_changed_datetime"]=jsondata.unread_notices_changed_datetime || 0;
					dataArray["notices_unread_count_new"]=jsondata.notices_unread_count_new || 0;
					dataArray["tickets_unread_count_new"]=jsondata.tickets_unread_count_new || 0;
					dataArray["tickets_unassigned_new"]=jsondata.tickets_unassigned_new || 0;
					dataArray["unread_submissions_changed_datetime"]=jsondata.unread_submissions_changed_datetime || 0;
					dataArray["submissions_unread_count_new"]=jsondata.submissions_unread_count_new || 0;
					dataArray["unread_pms_changed_datetime"]=jsondata.unread_pms_changed_datetime;
					dataArray["unread_private_messages_count_new"]=jsondata.unread_private_messages_count_new || 0;
					dataArray["menu_unread_count"]=jsondata.notices_unread_count || 0;
					dataArray["submissions_unread_count"]=jsondata.submissions_unread_count || 0;
					dataArray["unread_private_messages_count"]=jsondata.unread_private_messages_count || 0;
					
					user_notice_updatedetails(dataArray);
				
				},
				onFailure: function() { 
					
				}
			});
		} else {
			//Process site notice data.
			site_notice_updatedetails();
			
			//Display or hide the subnotices based on their counts.
			show_hide_notification_new_count("message_notification_new_count");
			show_hide_notification_new_count("submission_notification_new_count");
			show_hide_notification_new_count("private_messages_new_count");
			
			//Process user notices data.
			user_notice_updatedisplay();
		
		}
	}
	
	//Update details and appearance for the various notification counts and indicators.
	function user_notice_updatedetails(dataArray) {

		//Skip this process if HTML5 storage is not supported.
		if (!supports_storage) return true;
		
		//Add any new notice counts to the subnotice numbers, but only if the data is newer than the last time it was written. 
		//(This prevents multiple simultaneous tabs loading from tripping over each other).
		if (localStorage["message_notification_new_count_lastcheck"] * 1 < dataArray["unread_notices_changed_datetime"]) {
			localStorage["message_notification_new_count"] = dataArray["notices_unread_count_new"];
			localStorage["message_tickets_unread_notification_new_count"] = dataArray["tickets_unread_count_new"];
			localStorage["message_tickets_unassigned_notification_new_count"] = dataArray["tickets_unassigned_new"];
			localStorage["message_notification_new_count_lastcheck"] = dataArray["unread_notices_changed_datetime"];
		}
		
		if (localStorage["submission_notification_new_count_lastcheck"] * 1 < dataArray["unread_submissions_changed_datetime"]) {
			localStorage["submission_notification_new_count"] = dataArray["submissions_unread_count_new"];
			localStorage["submission_notification_new_count_lastcheck"] = dataArray["unread_submissions_changed_datetime"];
		}
		
		if (localStorage["private_messages_new_count_lastcheck"] * 1 < dataArray["unread_pms_changed_datetime"]) {
			localStorage["private_messages_new_count"] = dataArray["unread_private_messages_count_new"];
			localStorage["private_messages_new_count_lastcheck"] = dataArray["unread_pms_changed_datetime"];
		}
		
		//Display or hide the subnotices based on their counts.
		show_hide_notification_new_count("message_notification_new_count");
		show_hide_notification_new_count("submission_notification_new_count");
		show_hide_notification_new_count("private_messages_new_count");
		
		//Now set the new values in the storage system, then they will get used for comparison in user_notice_updatedisplay(); at the end of this code block.
		localStorage["notices_unread_count"] = dataArray["menu_unread_count"];
		localStorage["submissions_unread_count"] = dataArray["submissions_unread_count"];
		localStorage["unread_private_messages_count"] = dataArray["unread_private_messages_count"];
		
		user_notice_updatedisplay();
		
	}
	
	//Update user notice display status from storage data.
	//check_for_portal set to string "no_portal" will not try to run the Portal Notices check AJAX script even if it detects new notices. Default is true, which will run the check.
	function user_notice_updatedisplay() {
		
		//Abort if browser does not support storage.
		if (!supports_storage) return true;
		
		//Sanity check incoming values.
		localStorage["notices_unread_count"] = localStorage["notices_unread_count"] * 1 || 0;
		localStorage["submissions_unread_count"] = localStorage["submissions_unread_count"] * 1 || 0;
		localStorage["unread_private_messages_count"] = localStorage["unread_private_messages_count"] * 1 || 0;
		
		//Notices
		$('message_notification_unread_count').innerHTML = localStorage["notices_unread_count"] * 1 || 0;
		if (localStorage["notices_unread_count"] * 1 > 0) {
			if ($('message_notification').style.display == 'none') {
				Effect.Appear('message_notification');
			}
		} else {
			if ($('message_notification').style.display != 'none') {
				Effect.Fade('message_notification');
			}
		}
		//Notices TopBar
		/*
		$('message_notification_unread_count_topbar').innerHTML = localStorage["notices_unread_count"] * 1 || 0;
		if (localStorage["notices_unread_count"] * 1 > 0) {
			if ($('message_notification_topbar').style.display == 'none') {
				Effect.Appear('message_notification_topbar');
			}
		} else {
			if ($('message_notification_topbar').style.display != 'none') {
				Effect.Fade('message_notification_topbar');
			}
		}
		*/
		//Submissions
		$('submission_notification_unread_count').innerHTML = localStorage["submissions_unread_count"] * 1 || 0;
		if (localStorage["submissions_unread_count"] * 1 > 0) {
			if ($('submission_notification').style.display == 'none') {
				Effect.Appear('submission_notification');
			}
		} else {
			if ($('submission_notification').style.display != 'none') {
				Effect.Fade('submission_notification');
			}
		}
		//Submissions TopBar
		/*
		$('submission_notification_unread_count_topbar').innerHTML = localStorage["submissions_unread_count"] * 1 || 0;
		if (localStorage["submissions_unread_count"] * 1 > 0) {
			if ($('submission_notification_topbar').style.display == 'none') {
				Effect.Appear('submission_notification_topbar');
			}
		} else {
			if ($('submission_notification_topbar').style.display != 'none') {
				Effect.Fade('submission_notification_topbar');
			}
		}
		*/
		//Private Messages
		if (localStorage["unread_private_messages_count"] * 1 > 0) {
			$('privatemessage_notification').addClassName("privatemessageunread_notification");
			$('privatemessage_notification').removeClassName("privatemessageread_notification");
			$('private_messages_unread_count').innerHTML = localStorage["unread_private_messages_count"] * 1;
		} else {
			$('privatemessage_notification').addClassName("privatemessageread_notification");
			$('privatemessage_notification').removeClassName("privatemessageunread_notification");
			$('private_messages_unread_count').innerHTML = "...";
		}
		//Private Messages Topbar
		/*
		if (localStorage["unread_private_messages_count"] * 1 > 0) {
			$('privatemessage_notification_topbar').addClassName("privatemessageunread_notification_topbar");
			$('privatemessage_notification_topbar').removeClassName("privatemessageread_notification_topbar");
			$('private_messages_unread_count_topbar').innerHTML = localStorage["unread_private_messages_count"] * 1;
		} else {
			$('privatemessage_notification_topbar').addClassName("privatemessageread_notification_topbar");
			$('privatemessage_notification_topbar').removeClassName("privatemessageunread_notification_topbar");
			$('private_messages_unread_count_topbar').innerHTML = "...";
		}
		*/
	}
	
	//Show the "new notifications count" extra indicator, but only if the value to display is > 0.
	//Set global Interval objects (used to start and end the reminder flashing).
	notification_new_flash_interval_obj = new Array();
	function show_hide_notification_new_count(obj_id) {
		
		//Skip this if local storage not supported.
		if (!supports_storage) return true;
		
		//Sanity check stored value.
		localStorage[obj_id] = localStorage[obj_id] * 1 || 0;
		if ($(obj_id).innerHTML.strip()=="") {
			//If the value in the actual indicator div has never been set, then assume the page has been newly loaded. Use old value from storage.
			oldval = localStorage[obj_id] * 1 || 0;
		} else {
			//If the value in the actual indicator div has been set, then use it as the old value.
			oldval = ($(obj_id).innerHTML * 1);
		}
		
		//If we're on the PORTAL page and can see the checkNotices() function unique to that page, then run that portal messages update function, as we now know new messages are waiting.
		if (obj_id == "message_notification_new_count" && typeof(checkNotices) == typeof(Function)) {
			//Check to see if the number has changed since we last fired the checkNotices() function. This means that even if the "new messages" number is the same, we can see a date change meaning the value was updated.
			if (localStorage["message_notification_new_count_lastcheck"] > show_new_lastpolled_resultsdate) {
				checkNotices();
			}
		}
		
		if (localStorage[obj_id] && localStorage[obj_id] > 0) {
			$(obj_id).innerHTML = localStorage[obj_id] * 1 || 0;
			//Show the item if it's not already showing.
			if (show_newnotice_numbers && $(obj_id).style.display == 'none') {
				//Show the notifications and animate if required.
				$(obj_id).show();
				clearInterval(notification_new_flash_interval_obj[obj_id]);
				//Flash the item if it is the first time is is being shown.
				if (oldval==0) Effect.Pulsate(obj_id, {from: 0.6});
				//Make the notification flash every X seconds to grab the user's attention.
				//TEMPORARILY DISABLED notification_new_flash_interval_obj[obj_id] = setInterval("Effect.Pulsate('"+obj_id+"', {from: 0.6});", 10000);
			}
			return true;
		} else {
			$(obj_id).innerHTML = localStorage[obj_id] * 1 || 0;
			//skip hiding the item if it's already hiden.
			if ($(obj_id).style.visibility == 'hidden' || $(obj_id).style.display == 'none') return true;
			clearInterval(notification_new_flash_interval_obj[obj_id]);
			$(obj_id).hide();
			return true;
		}
	}
	
	//Update site notice details from storage data.
	function site_notice_updatedetails() {
		
		//Abort if browser does not support storage.
		if (!supports_storage) return true;
		
		//Use cookie values to update message and status.
			//Should the site notice box be displayed?
			if (localStorage["site_status_notice_on"]=='yes' && localStorage["site_status_dismiss"]!='yes') {
				//Update site notice text.
				$('site_notice_text').innerHTML = localStorage["site_status_notice_text_bbcode_parsed"];
				//Check if site notice is already showing. Show if not.
				if (!$('site_notice').hasClassName('site_notice_active')) site_notice_show(true);
				
				//Show offline notice? (Replaces timer)
				if (localStorage["site_status_maintmode"]=='yes') {
					$('site_notice_countdown').removeClassName('site_notice_countdown_on');
					site_notice_show_offline();
				} else {
					site_notice_hide_offline();
				}
			
				//Should the timer be started?
				if (localStorage["site_status_time_on"]=='yes' && localStorage["site_status_maintmode"]!='yes') {
					$('site_notice_countdown_targettime_epoch').innerHTML=localStorage["site_status_event_datetime_epoch"];
					
					//Convert system epoch to local human readable time in format h:m(am|pm) eg: 4:59pm.
					var myDate = new Date(localStorage["site_status_event_datetime_epoch"] * 1000);
					tmp_newdatetime = (myDate.getHours() % 12 == 0 ? 12 : myDate.getHours() % 12) + ":" + ( myDate.getMinutes() < 10 ? "0" : "" ) + myDate.getMinutes() + ((myDate.getHours() < 12) ? 'am' : 'pm');
					
					$('site_notice_countdown_date_text').innerHTML= tmp_newdatetime; 
					if (!timervar) site_notice_startTimer();
					if (!$('site_notice_countdown').hasClassName('site_notice_countdown_on')) $('site_notice_countdown').addClassName('site_notice_countdown_on');
				} else {
					$('site_notice_countdown').removeClassName('site_notice_countdown_on');
				}
			} else {
				if ($('site_notice').hasClassName('site_notice_active')) site_notice_hide();
			}
	}
	
	//Initialise site notice checking via Cookies (check them every 10 seconds) and AJAX at interval (milliseconds)
	//Shares cookies between all site windows so checking will only ever be performed ONCE per interval for all windows combined.
	function site_notice_checkmessage_init(interval) {
		setInterval("site_notice_checkmessage("+interval+")", 10000);
	}

	//Ajax call to increment view count for current submission. 
	//Can only be called from submissionview.php page as the target script uses referrer to determine submission ID.
	function incrementViewcount() {
		new Ajax.Request('/incrementsubmissionviews_process.php',
		{
			parameters: { token: global_token },
			requestHeaders: {Accept: 'application/json'},
			onSuccess: function(transport){
			},
			onFailure: function() {
			}
		});
	}
	
/* JUNK code while testing quick nav buttons. Kept for when we implement them properly.
	var nav_on = true;
	var nav_show_duration = 0.3;
	var nav_fade_min = 0;
	var nav_fade_max = 0.5;
	var nav_topcopy_showing = false;
	var nav_bottomcopy_showing = false;
	
	var nav_viewport;
	var nav_viewport_width;
	var nav_activation_border_x;
	var nav_window_scrollpos = 0;
	
	//Initialise the properties for the quick page nav button show/hide feature at least once epr page load.
	navInitWindow();
	
	//Adjust the quick page nav button show/hide feature environment properties any time the window is resized.
	addEvent(window, 'resize', navInitWindow);
	
	//Initialise the properties for the quick page nav button show/hide feature.
	function navInitWindow() {
		nav_viewport = document.viewport.getDimensions(); // Gets the viewport as an object literal
		nav_viewport_width = nav_viewport.width; // Gets the viewport width
		nav_viewport_height = nav_viewport.height; // Gets the viewport height
		nav_activation_border_x = nav_viewport.width - 200; //nav_viewport.width - ((nav_viewport.width - 960) / 2); //Get the width of the right margin/gutter to use as hot area to show buttons when mouse is over it.
		nav_activation_border_y = nav_viewport.height / 2; // Border edge to differentiate upper and lower halves of window.
		
		//Only activate the nav buttons if there is enough room for them in the right margin.
		nav_on = (nav_viewport_width >= 960 + 50 + 50);
		
		if (!nav_on && $('navto_pagetop1')) {
			nav_topcopy_showing = false;
			nav_bottomcopy_showing = false;
			$('navto_pagetop1').hide();
			$('navto_pagebottom1').hide();
			$('navto_pagetop2').hide();
			$('navto_pagebottom2').hide();
		}
	}
	
	//As the mouse is moved, check where it is in the browser window. If it's in the hot zone to show the quick nave buttons, show them, otherwise ensure they are hidden.
	function navCheckPos(e)
	{
		//If nav is disabled (screen too narrow) or nav objects not read, then skip this.
		if (!nav_on || !$('navto_pagetop1')) return false;
				  
		//Get mouse position.
		x = Event.pointerX(e);
		y = Event.pointerY(e);
		//Adjust Y value to allow for page scroll amount. pointerY gets mouse position within document body not window viewport.
		y = y - document.viewport.getScrollOffsets().top;

		//Check if mouse in activation area for top nav buttons copy.
		if (x >= nav_activation_border_x) {
			if (y < nav_activation_border_y) {
				if (!nav_topcopy_showing) {
					nav_topcopy_showing = true;
					showNavTop();
				}
			} else {
				if (nav_topcopy_showing) {
					nav_topcopy_showing = false;
					hideNavTop();
				}
			}
			
		} else {
			if (nav_topcopy_showing) {
				nav_topcopy_showing = false;
				hideNavTop();
			}
		}
		
		//Check if mouse in activation area for top nav buttons copy.
		if (x >= nav_activation_border_x) {
			if (y > nav_activation_border_y) {
				if (!nav_bottomcopy_showing) {
					nav_bottomcopy_showing = true;
					showNavBottom();
				}
			} else {
				if (nav_bottomcopy_showing) {
					nav_bottomcopy_showing = false;
					hideNavBottom();
				}
			}
			
		} else {
			if (nav_bottomcopy_showing) {
				nav_bottomcopy_showing = false;
				hideNavBottom();
			}
		}
	}
	//Fire this function any time the mouse is moved.
	addEvent(window, 'mousemove', navCheckPos);
	
	//Show/hide quick page navigation buttons with a fad in/out effect.
	function showNavTop() {
		Effect.Appear('navto_pagetop1', { duration: nav_show_duration, from: nav_fade_min, to: nav_fade_max } );
		Effect.Appear('navto_pagebottom1', { duration: nav_show_duration, from: nav_fade_min, to: nav_fade_max } );
	}
	function showNavBottom() {
		Effect.Appear('navto_pagetop2', { duration: nav_show_duration, from: nav_fade_min, to: nav_fade_max } );
		Effect.Appear('navto_pagebottom2', { duration: nav_show_duration, from: nav_fade_min, to: nav_fade_max } );
	}
	function hideNavTop() {
		Effect.Fade('navto_pagetop1', { duration: nav_show_duration, from: nav_fade_max, to: nav_fade_min } );
		Effect.Fade('navto_pagebottom1', { duration: nav_show_duration, from: nav_fade_max, to: nav_fade_min } );
	}
	function hideNavBottom() {
		Effect.Fade('navto_pagetop2', { duration: nav_show_duration, from: nav_fade_max, to: nav_fade_min } );
		Effect.Fade('navto_pagebottom2', { duration: nav_show_duration, from: nav_fade_max, to: nav_fade_min } );
	}
*/
	
	
	//Common widget Javascript goes here. This is always loaded in the head section of every page that includes the HTML header file "header.php".


//Jump to page function used by Paginator.
function jumptopage(url,page,afterurl) {
	afterurl = afterurl || '';
	window.location=url+page+afterurl;
}

//Get caret position in a text input.
function getCaretPos(obj) {
		
		// Initialize
		var iCaretPos = 0;

		// IE Support
		if (document.selection) {

		// Set focus on the element
		obj.focus ();

		// To get cursor position, get empty selection range
		var oSel = document.selection.createRange ();

		// Move selection start to 0 position
		oSel.moveStart ('character', -obj.value.length);

		// The caret position is selection length
		iCaretPos = oSel.text.length;
		}

		// Firefox support
		else if (obj.selectionStart || obj.selectionStart == '0')
		iCaretPos = obj.selectionStart;

		// Return results
		return (iCaretPos);

	}

//Set position of caret in a text input.
function setCaretTo(obj, pos) {

	if(obj.createTextRange) {
		var range = obj.createTextRange();
		range.move('character', pos);
		range.select();
	} else if(obj.selectionStart) {
		obj.focus();
		obj.setSelectionRange(pos, pos);
	}
}

//Get the text that is selected in a textarea
function getSelText(textArea) {
	if (document.selection) { //IE
		var bm = document.selection.createRange().getBookmark();
		var sel = textArea.createTextRange();
		sel.moveToBookmark(bm);

		var sleft = textArea.createTextRange();
		sleft.collapse(true);
		sleft.setEndPoint("EndToStart", sel);
		
		var selectionStart = sleft.text.length
		var selectionEnd = sleft.text.length + sel.text.length;
		
		var selectedText = sel.text;
		
	}
	else { //FF

		var selectionStart = textArea.selectionStart;
		var selectionEnd = textArea.selectionEnd;
		
		var selectedText = textArea.value.substring(textArea.selectionStart,textArea.selectionEnd);
		
	}
	
	var out = new Array();
	
	if (!selectedText) var selectedText = '';
	
	out[0] = selectedText;
	out[1] = selectionStart;
	out[2] = selectionEnd;
	
	return out;
}

//Set position of cursor and a selected range (set selStart and selEnd to the same value to position the cursor with no selected text).
//inputEl is the textarea object.
function setSelRange(inputEl, selStart, selEnd) { 
	if (inputEl.setSelectionRange) { 
		inputEl.focus(); 
		inputEl.setSelectionRange(selStart, selEnd); 
	} else if (inputEl.createTextRange) { 
		var range = inputEl.createTextRange(); 
		range.collapse(true); 
		range.moveEnd('character', selEnd); 
		range.moveStart('character', selStart); 
		range.select(); 
	} 
}

//Insert new text at the position of the caret in a textarea.
function insertAtCaret(obj, text) {
	if(document.selection) {
		obj.focus();
		var orig = obj.value.replace(/\r\n/g, "\n");
		var range = document.selection.createRange();

		if(range.parentElement() != obj) {
			return false;
		}

		range.text = text;
		
		var actual = tmp = obj.value.replace(/\r\n/g, "\n");

		for(var diff = 0; diff < orig.length; diff++) {
			if(orig.charAt(diff) != actual.charAt(diff)) break;
		}

		for(var index = 0, start = 0; 
			tmp.match(text) 
				&& (tmp = tmp.replace(text, "")) 
				&& index <= diff; 
			index = start + text.length
		) {
			start = actual.indexOf(text, index);
		}
	} else if(obj.selectionStart) {
		var start = obj.selectionStart;
		var end   = obj.selectionEnd;

		obj.value = obj.value.substr(0, start) 
			+ text 
			+ obj.value.substr(end, obj.value.length);
	}
	
	if(start != null) {
		//setCaretTo(obj, start + text.length);
	} else {
		obj.value += text;
	}
}

//Wrap the selected text in a textarea in the given start and end text (such as wrapping selection in tags).
function wrapSelText(textarea_id, starttext, endtext) {
	Xtextarea = $(textarea_id);
	Xtextarea_scroll = Xtextarea.scrollTop;
	
	var sarray = getSelText(Xtextarea); //(Xtextarea.value).substring(Xtextarea.selectionStart, Xtextarea.selectionEnd); 
	var seltxt = sarray[0];
	var selectionStart = sarray[1];
	var selectionEnd = sarray[2];

	if (document.selection && !seltxt) {
		//HACK for IE... if we are inserting WITHOUT SELECTED TEXT we need to run this function instead.
		insertAtCaret(Xtextarea, starttext+endtext);
	} else {

		var newtext = starttext + seltxt + endtext;
		
		var start = (Xtextarea.value).substring(0, selectionStart);
		var end = (Xtextarea.value).substring(selectionEnd, Xtextarea.value.length);
		
		Xtextarea.value = start + newtext + end;
		
		//Position the cursor at the end of the selected text, offset to the right to account for the length of the inserted BBCode characters.
		textlen = starttext.length+endtext.length;
		setSelRange(Xtextarea, selectionEnd+textlen, selectionEnd+textlen);
	}
	
	Xtextarea.scrollTop = Xtextarea_scroll;

	//If the text length checker function is present, update the length count.
	if (update_len) update_len();
	
	return true;
}

//Comma helper regex to ensure commas are used to separate words and underscores join words in place of spaces in a text input element.
globalfocus = ''; //Get currently focussed object on page. Useful for deciding if we need to perform functions that should only happen when the object is focussed, like a textarea etc.
commaspacertimeout = new Array;
function commaHelper(obj) {
	
	globalfocus = obj.id;
	
	//Set an event ready to clear fcoussed item string when we leave the text input. Used to prevent the cursor from jumping back to this input as our script updates commas etc.
	addEvent(obj, 'blur', commaUnFocus(obj.id));
	
	var curpos = getCaretPos(obj);

	var origlen = obj.value.length;
	
	var newstring = obj.value.toString();
	
	newstring = newstring.replace(/([^ +,]) +([^ +,$])/g, "$1,$2");
	
	var newlen = newstring.length;
	
	var offset = newlen - origlen;
	
	//Only update text on actual change or else it will mess with just trying to select/highlight text.
	if (newstring != obj.value.toString()) {
		obj.value = newstring;
		setCaretTo(obj, curpos + offset);
	}
	
	if (commaspacertimeout[obj.id]) clearTimeout(commaspacertimeout[obj.id]);
	commaspacertimeout[obj.id] = setTimeout('commaHelperSpacer(\''+obj.id+'\')',1000);
	
}
//Additional function used by commaHelper() to pad out commas with a space after a short timeout.
function commaHelperSpacer(objid) {
	
	obj = $(objid);
	
	var curpos = getCaretPos(obj);

	var origlen = obj.value.length;
	
	var newstring = obj.value.toString();
	
	newstring = newstring.replace(/([^ +,]),([^ +,]|$)/g, "$1, $2");
	
	var newlen = newstring.length;
	
	var offset = newlen - origlen;
	
	//Only update text on actual change or else it will mess with just trying to select/highlight text.
	if (newstring != obj.value.toString()) {
		obj.value = newstring;
		//Only set carat position if the text element is still focussed.
		if (globalfocus == obj.id) setCaretTo(obj, curpos + offset);
	}
}
//Reset focussed item to null as we leave each text input that has the comma helper enabled on it.
function commaUnFocus(objid) {
	globalfocus = '';
}

// End widgets

var bgcookie;
var clientx = document.viewport.getWidth();
var clienty = document.viewport.getHeight();
var devicePixelRatio = window.devicePixelRatio || 1;
var fullcookie;
var Cclientscreenpair;
var Cclientscreen;
var Cclientscreenx;
var Cclientscreeny;
var Cserverscreenpair;
var Ctimeofday;
var Cbgtheme;

//Always run bginit on page load.
addEvent(window, 'load', bg_init);

function bg_init() {
	//If on WebKit/Chromium, set a null transform on the body element to push it onto a separate layer,
	//ensuring that we don't repaint the background and foreground all the time.
	//Other browser engines (e.g. Gecko) tend to handle this better.
	if (Prototype.Browser.WebKit) {
		$('bgbox').style.WebkitTransform='translateZ(0)';
		$('bgbox').style.transform='translateZ(0)';
	}
	//Check if there is a screen res, theme and time of day cookie.
	bgcookie = getCustomCookie('background');
	clientx = document.viewport.getWidth();
	clienty = document.viewport.getHeight();
	
	devicePixelRatio = window.devicePixelRatio || 1;
	
	//Resize submission picture if present.
	if (typeof magicbox_curmode !== 'undefined') magicBoxResize();
	
	valid_bgcookie = false;
	Cserverscreen = false;
	
	fullcookie = bgcookie.split(',');
	if (fullcookie.constructor === Array) {
		fullcookie_count = fullcookie.length;
		
		//Validate background cookie data.
		//Check if there are 4 values in the cookie separated by commas.
		if (bgcookie && fullcookie_count == 4) {
			//Run other validation checks on each element of the cookie data array.
			Cclientscreenpair = fullcookie[0];
			Cclientscreen = Cclientscreenpair.split('x');
				if (Cclientscreen.length == 2) {
					if (Cclientscreen[0] * 1 > 0 && Cclientscreen[1] * 1 > 0) {
						Cserverscreenpair = fullcookie[1];
						Cserverscreen = Cserverscreenpair.split('x');
						if (Cserverscreen.length == 2) {
							if (Cserverscreen[0] * 1 > 0 && Cserverscreen[1] * 1 > 0) {
								if (fullcookie[2] == "100% auto" || fullcookie[2] == "auto 100%") {
									if (fullcookie[3] && !isNaN(fullcookie[3])) {
										valid_bgcookie = true;
									}
								}
							}
						}
					}
				}
		}
	}
	
	if (valid_bgcookie) {
		
		//Check bg image sizes against valid sizes for the current theme.
		foundone = false;
		bgtheme_image_sizes_array = bgtheme_image_sizes.split(',');
		bgtheme_image_sizes_array.each(function(pair) {
			resolutions = pair.split('x');
			if (Cserverscreen && Cserverscreen.length == 2 && resolutions[0] == Cserverscreen[0] && resolutions[1] == Cserverscreen[1]) {
				foundone = true;
			}
		});
		
		//Check if the current viewport res matches what is stored in the cookie.
		Cclientscreenx = Cclientscreen[0];
		Cclientscreeny = Cclientscreen[1];
		
		//If any invalid values are found, request the background data again.
		if (Cclientscreenx!=clientx || Cclientscreeny!=clienty || devicePixelRatio != fullcookie[3] || !foundone) {
			getBackground(clientx, clienty, global_timeofday, global_bgtheme, devicePixelRatio, 'no');
		}
		
	} else {

		//There was no valid background cookie. Get background details from the server.
		//Skip if background is set to gray/none.
		if (global_bgtheme != 'none') getBackground(clientx, clienty, global_timeofday, global_bgtheme, devicePixelRatio, 'no');
	}
}

//Global var to store the background init string for reuse.
global_bgstring = '';
if (getCustomCookie('background') && getCustomCookie('background')!='') global_bgstring = getCustomCookie('background');

function getBackground(screenx, screeny, timeofday, bgtheme, dpr, save) {

	new Ajax.Request('/background.php',
		{
			parameters: { token:global_token, x:screenx, y:screeny, p: dpr, timeofday:timeofday, bgtheme:bgtheme, save:save },
			requestHeaders: {Accept: 'application/json'},
			onSuccess: function(transport){
				var response = transport.responseText || "{'error':'no response'}";
				var jsondata = response.evalJSON(true);
								
				if (jsondata && jsondata.background) {

					// Set on <head> rather than <body> so we can apply a transform to the body for Chrome background scaling performance
					$('bgbox').style.background = jsondata.background;
					//Apply background-size scale seperately to keep IE8 happy.
					$('bgbox').style.backgroundSize = jsondata.scale;
					if (jsondata.description) { 
						$('bg_footer').innerHTML = jsondata.description;
						tmpdesc = jsondata.description;
					} else {
						$('bg_footer').innerHTML = 'None.';
						tmpdesc = '';
					}
					
					//Set screen res, theme and timeofday cookie
					
					global_bgstring= screenx+'x'+screeny+','+jsondata.imagex+'x'+jsondata.imagey+','+jsondata.scale+','+devicePixelRatio;
					setCustomCookie('background', global_bgstring,1);
					
					if (global_bgtheme != jsondata.bgtheme) {
						global_bgtheme=jsondata.bgtheme;
						showBackgroundLoadmessage(" " + jsondata.description);
					}
					
					global_timeofday=jsondata.timeofday;
				}
			},
			onFailure: function() { 
				
			}
		});
}


resizetimeout = null;
function resetGlobalBGInitString() {
	
	//In case we have two or more windows on two or more different monitor sizes, keep resetting the apparent view width/height cookie so the last window clicked or resized transmits the right size as the request is made.
	setCustomCookie('background', global_bgstring, 1);
	
	newclientx = document.viewport.getWidth();
	newclienty = document.viewport.getHeight();
	
	newdevicePixelRatio = window.devicePixelRatio || 1;
	
	//If viewport size has changed.
	if (newclientx != clientx || newclienty != clienty || newdevicePixelRatio != devicePixelRatio) {
		
		//Update viewport size.
		fullcookie = global_bgstring.split(',');
		Cclientscreenpair = fullcookie[0];
		Cserverscreenpair = fullcookie[1];
		Cscale = fullcookie[2];
		CdevicePixelRatio = fullcookie[3];
		
		clientx = newclientx;
		
		clienty = newclienty;
		
		devicePixelRatio = newdevicePixelRatio;
		
		//Update viewport size in the cookie string.
		global_bgstring = clientx + "x" + clienty + "," + fullcookie[1] + "," + fullcookie[2] + "," + devicePixelRatio;
	
		setCustomCookie('background', global_bgstring, 1);
		
		//Update background (in case a more optimal size image is now appropriate to the viewport size). Do this on a timeout delay since last time the viewport size changed so we don't fire it a zillion times as viewport is dragged.
		clearTimeout(resizetimeout);
		resizetimeout = setTimeout(function() { if (typeof magicbox_curmode !== 'undefined') magicBoxResize(); getBackground(clientx, clienty, global_timeofday, global_bgtheme, devicePixelRatio, 'no') }, 1500);
		
	}
}
//Check if window changed size and update background if needed.
addEvent(window, 'resize', resetGlobalBGInitString);
//In case we have two or more windows on two or more different monitor sizes, keep resetting the apparent view width/height cookie so the last window interacted with or resized transmits the right background cookie string as the request is made.
addEvent(window, 'mousedown', resetGlobalBGInitString);
addEvent(window, 'keydown', resetGlobalBGInitString);

//Show the "loading" message for a brief period of time.
var bgbrowser_loadmsg_timeout;
function showBackgroundLoadmessage(extratext) {
	clearTimeout(bgbrowser_loadmsg_timeout);
	$('bgbrowser_loadmsg').innerHTML = "Loading..." + (extratext || "");
	$('bgbrowser_loadmsg').setStyle({display: 'inline'});
	bgbrowser_loadmsg_timeout = setTimeout('$("bgbrowser_loadmsg").setStyle({display: "none"})', 3000);
}

//Change to the previous background in the available list.
function changebgPrev() {
	if (!bg_array[bg_index-1]) { 
		bg_index = bg_array.length-1;
	} else {
		bg_index = bg_index - 1;
	}
	showBackgroundLoadmessage();
	getBackground(clientx, clienty, '', bg_array[bg_index], devicePixelRatio, 'yes');
}

//Change to the next background in the available list.
function changebgNext() {
	if (!bg_array[bg_index+1]) {
		bg_index = 0;
	} else {
		bg_index = bg_index + 1;
	}
	showBackgroundLoadmessage();
	getBackground(clientx, clienty, '', bg_array[bg_index], devicePixelRatio, 'yes');
}

function getBanner(command) {
	
	new Ajax.Request('/bchanger.php?',
		{
			parameters: { token:global_token, command: command },
			requestHeaders: {Accept: 'application/json'},
			onSuccess: function(transport){
				var response = transport.responseText || "{'error':'no response'}";
				var jsondata = response.evalJSON(true);
								
				if (jsondata && jsondata.banner_num) {

					sbanner = jsondata.banner_num;
					
					//Set banner by new number here.
					$('banner_panel').className='inkbunny_navigation pic'+sbanner;
				}
			},
			onFailure: function() { 
				
			}
		});
}

//Show the "loading" message for a brief period of time.
var banner_loadmsg_timeout;
function showBannerLoadmessage() {
	clearTimeout(banner_loadmsg_timeout);
	$('bannerbrowser_loadmsg').setStyle({display: 'block'});
	banner_loadmsg_timeout = setTimeout('$("bannerbrowser_loadmsg").setStyle({display: "none"})', 1500);
}

//Change to the previous banner in the available list.
function changeBannerPrev() {
	showBannerLoadmessage();
	getBanner('prev');
}

//Change to the next banner in the available list.
function changeBannerNext() {
	showBannerLoadmessage();
	getBanner('next');
}

//Change to a random banner in the available list.
function changeBannerRandom() {
	showBannerLoadmessage();
	getBanner('rand');
}

//Set pool pool_id as active pool in special cookie.
function setActivePool(pool_id) {
	setCustomCookie('p',pool_id,1);
}

//Functions regarding submissions.
function submission_exactdate() {
	$('submittime_elapsed').setStyle({display: "none"});
	$('submittime_exact').setStyle({display: ""});
}

function submission_elapseddate() {
	$('submittime_elapsed').setStyle({display: ""});
	$('submittime_exact').setStyle({display: "none"});
}

function showMD5() {
	$('md5box').setStyle({display: "block"});
	return false;
}

function showBBCodeThumbs() {
	$('bbcodethumbbox').setStyle({display: "block"});
	return false;
}

//Start submissionview magicbox
function checkStandardDef() {
	if (!standarddefloaded && !standarddefloading) {
		
		//Set standarddefloading true so we don't keep trying to load it over and over before it's finished.
		standarddefloading = true;
		
		$("magicboxLoaderText").innerHTML = "Loading SD...";
		
		$("magicboxLoader").setStyle({"display":"block"});
		
		addEvent($("magicboxLoaderImage"), 'load', function(){ if (highdefloaded || highdefloading) return true; $("magicbox").src=$("magicboxLoaderImage").src; $("magicboxLoader").setStyle({"display":"none"}); standarddefloaded=true; standarddefloading=false;});
		
		$("magicboxLoaderImage").src = standarddefURL;
	}
}

function checkHighDef() {
	
	//Only switch to high def image if the original is larger than currently displayed image, and larger than 920px wide.
	//Remove shadow while determining image size.
	$("magicbox").removeClassName("shadowedimage");
	curwidth = $('magicbox').getWidth();
	curheight = $('magicbox').getHeight();
	$("magicbox").addClassName("shadowedimage");
	if (origwidth == classicwidth || curwidth >= origwidth || curheight >= origheight) return;
	
	if (!highdefloaded && !highdefloading) {
		
		//Set highdefloading true so we don't keep trying to load it over and over before it's finished.
		highdefloading = true;
	
		$("magicboxLoaderText").innerHTML = "Loading HD...";
		
		//Only show loading box if we're not in wide mode by default. This stops redundant loading indicator when it loads HD image by default on page load.
		if (submission_size_view != 2) {
			$("magicboxLoader").setStyle({"display":"block"});
		}
		
		addEvent($("magicboxLoaderImage"), 'load', function(){$("magicbox").src=$("magicboxLoaderImage").src; $("magicboxLoader").setStyle({"display":"none"}); highdefloaded=true; highdefloading=false;});
		
		//Clear SRC in case the standard definition image is currently loading. This forces the browser to cancel loading the standard def image.
		//Also fixes a bug where the browser will keep displaying the standard def image loading progress instead of high def image.
		$("magicboxLoaderImage").src = '';
		
		$("magicboxLoaderImage").src = highdefURL;
	}
}
function magicBoxFit(mode) {
	switch (mode) {
		case 'wide':
			checkHighDef();
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: "100%" });
			});
			maxwidth = origwidth;
			if (maxwidth > clientx) maxwidth = clientx;
			$("magicbox").setStyle({ "max-height": "none", "max-width": maxwidth + "px", height: "auto", width: maxwidth + "px" });
			parentwidth = maxwidth + 20;
			if (parentwidth < 960) parentwidth = 960;
			if (parentwidth > clientx) parentwidth = clientx;
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: (parentwidth) + "px" });
			});
			break;
		case 'fit':
			checkHighDef();
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: "100%" });
			});
			maxheight = origheight;
			//Keep height within sane limits.
			if (maxheight > clienty) maxheight = clienty;
			//Calculate the new width preserving the original aspect ratio.
			maxwidth = (maxheight / origheight) * origwidth;
			//Keep width within sane limits.
			if (maxwidth > clientx) {
				maxwidth = clientx;
				//Preserve aspect ratio as we just changed image size again.
				maxheight = (maxwidth / origwidth) * origheight;
			}
			$("magicbox").setStyle({ "max-height": maxheight + "px", "max-width": maxwidth + "px", height: maxheight + "px", width: maxwidth + "px"});
			parentwidth = maxwidth + 20;
			if (parentwidth < 960) parentwidth = 960;
			if (parentwidth > clientx) parentwidth = clientx;
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: (parentwidth) + "px" });
			});
			break;
		case 'classic':
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: "100%" });
			});
			maxwidth = classicwidth;
			if (maxwidth > 920) maxwidth = 920;
			if (smallmode) {
				$("magicbox").setStyle({ "max-height": "300px", "max-width": "300px", height: "auto", width: "auto" });
			} else {
				$("magicbox").setStyle({ "max-height": "none", "max-width": maxwidth + "px", height: "auto", width: maxwidth + "px" });
			}
			parentwidth = 960;
			$$(".magicboxParent").each(function (ob) {
				ob.setStyle({ width: (parentwidth) + "px" });
			});
			break;
	}
	$("magicboxControl"+mode.capitalize()).setStyle({ display: "block"});
	magicbox_curmode = mode;
	
	if (parentwidth > 960 && parentwidth != clientx) {
		$$(".magicboxOuter").each(function (ob) { ob.setStyle({borderRadius: "15px"});
		});
	} else {
		$$(".magicboxOuter").each(function (ob) { ob.setStyle({borderRadius: "0"});
		});
	}

}

function magicBoxResize() {
	magicBoxFit(magicbox_curmode);
}

function scrollToMe(obj) {
	var curtop = 0;
	if (obj.offsetParent) {
		do {
		curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
		return [curtop];
	}
}

function magicBoxSwitch() {
	
	//If image is in "small" mode then this is the first time size change has been called by user click.
	if (smallmode) {
		smallmode = false;
		magicbox_curmode = 'classic';
		//Change image src to "screen" size.
		checkStandardDef();
	} else {
	
		switch (magicbox_curmode) {
			case 'classic':
				magicbox_curmode = 'wide';
				break;
			case 'wide':
				magicbox_curmode = 'fit';
				break;
			case 'fit':
				magicbox_curmode = 'classic';
				break;
		}
	}
	
	$("magicboxControl").setStyle({ display: "none" });
	$("magicboxControlClassic").setStyle({ display: "none"});
	$("magicboxControlWide").setStyle({ display: "none"});
	$("magicboxControlFit").setStyle({ display: "none"});
	
	magicBoxFit(magicbox_curmode);
	if ('fit' === magicbox_curmode) {
		Object.isFunction($('magicbox').scrollIntoViewIfNeeded) ? $("magicbox").scrollIntoViewIfNeeded() : window.scroll(0,scrollToMe($("magicbox")));
	}
	
	$("magicboxZoomNumber").innerHTML = Math.round((maxwidth / origwidth) * 100) + "%";
	
	if (fadecontrol) fadecontrol.cancel();
	
	$("magicboxControl").setStyle({ display: "block", opacity: 0.9 });
	
	fadecontrol = new Effect.Fade('magicboxControl', { from: 0.9, to: 0, delay: 2, duration: 1.0 });
}
//End submissionvew magicbox
