function clearField(elt) {
	if(elt.defaultValue == elt.value) elt.value = '';
}

function popupWin(url,w_name,w_width,w_height) {
	window.open(url,w_name,'toolbar=yes,location=yes,directories=no,status=yes,menubar=no,scrollbars=yes,resizable=yes,width='+w_width+',height='+w_height);
	return false;
}

function sbInit() {
	eltMsgForm = document.forms['msgform'];
	if(document.images) {
		eltSwitch = document.images['rfswitch'];

		if(rfSwitchPosition == 1) {
			eltSwitch.style.backgroundPosition = '-16px 0px';
		}
	}

	var scrollPos=999999, allowFocus=true;

	if(hlShoutId) {
		var eltHlShout = document.getElementById(hlShoutId);
		if(eltHlShout && typeof eltHlShout.offsetParent != 'undefined' && typeof document.documentElement.clientHeight == 'number') {
			var posY = 0 - Math.round(document.documentElement.clientHeight / 2);
			for(var oElement = eltHlShout; oElement; oElement = oElement.offsetParent) {
				posY += oElement.offsetTop;
			}
			scrollPos = (posY > 0) ? posY : 0;
			allowFocus = false;
		}
	}
	scrollTo(0, scrollPos);

	if(refreshMethod != 'formsubmit') {
		if(typeof XMLHttpRequest == 'undefined') {
			// Force old browsers to use old refresh method
			refreshMethod = 'formsubmit';
		}
		else {
			eltOnline = document.getElementById('usersonline');
			eltShouts = document.getElementById('shouts');

			if(eltOnline && eltShouts) {
				sbUpdateCurrentTime();

				if(allowFocus) setTimeout(function() { eltMsgForm.elements['message'].focus(); }, 100);
			} else {
				// Something went wrong, revert to old method
				refreshMethod = 'formsubmit';
			}
		}
	}

	var eltUnhidden = eltMsgForm.elements['unhidden_shouts'];
	if(eltUnhidden) {
		// Load shout IDs into memory if necessary, then reset the form field
		if(refreshMethod == 'formsubmit' && eltUnhidden.value) {
			unhiddenShouts = eltUnhidden.value.split(',');
		}
		eltUnhidden.value = '';
	}

	sbLinkifyIgnored();
}

function rfSwitch() {
	if(!eltSwitch) return;
	if(rfSwitchPosition != 0) { rfSwitchPosition = 0; eltSwitch.style.backgroundPosition = '0px 0px'; clearTimeout(timeoutR); eltMsgForm.elements['rf'].value = '0'; }
	else { rfSwitchPosition = 1; eltSwitch.style.backgroundPosition = '-16px 0px'; timeoutR = setTimeout(sbRefresh, Math.ceil(refreshRate/2)*1000); eltMsgForm.elements['rf'].value = '1'; }
}

function readCDATA(elt) {
	if(!elt || !elt.firstChild) return false;

	var node = elt.firstChild, content = '';
	do { if(node.nodeName == '#cdata-section' && node.data) content += node.data; } while(node = node.nextSibling);
	return content;
}

function sbRefresh() {
	if(refreshMethod == 'formsubmit') {
		if(eltMsgForm) {
			var eltMessage = eltMsgForm.elements['message'];
			if(eltMessage && eltMessage.value == eltMessage.defaultValue && eltMsgForm.elements['rf'].value == '1') {
//				if(eltSwitch) eltSwitch.src = imgRefreshAct.src; // Show activity indicator, and just leave it there =p

				if(unhiddenShouts.length > 0) eltMsgForm.elements['unhidden_shouts'].value = unhiddenShouts.join(',');

				eltMsgForm.submit();
			}
		}
		return;
	}

	var submitMode = !!queuedShout;
	var rfEnabled = (!submitMode || rfSwitchPosition != 0); // sbRefresh() may have been called to submit a shout while refresh was disabled

	if(rfEnabled) timeoutR = setTimeout(sbRefresh, refreshRate*1000);

	if(loadingNow) return; // Prevent more than one XMLHttpRequest from being open at once
	loadingNow = true;

	var shoutToSend = null;
	if(submitMode) {
		shoutToSend = queuedShout;
		queuedShout = null;
	}
	var submitModeStr = (submitMode) ? 'submit' : 'refresh';

	sbUpdateThrobber(true, rfEnabled);

	var scrolledToBottom = true;

	if(typeof window.pageYOffset == 'number') {
		var maxScroll = Number.NaN;

		if(typeof window.scrollMaxY == 'number') {
			// Works in Firefox
			maxScroll = window.scrollMaxY;
		} else if(typeof document.documentElement.scrollHeight == 'number' && typeof document.documentElement.clientHeight == 'number') {
			// Works in Opera
			maxScroll = document.documentElement.scrollHeight - document.documentElement.clientHeight;
		}
		if(!isNaN(maxScroll) && window.pageYOffset < maxScroll) {
			scrolledToBottom = false;
		}
	}

	var x = new XMLHttpRequest();

	var requestTimer = setTimeout(function() {
		if(x.readyState > 0 && x.readyState < 4) {
			x.abort();
			loadingNow = false;
			eltOnline.innerHTML = '<strong>Download timed out!</strong>';
			sbHandleQueuedShout();
		}
	}, (refreshRate+1)*1000);

	x.onreadystatechange = function() {
		if(x.readyState != 4) return;

		clearTimeout(requestTimer);

		if(x.status != 200) {
			if(x.status == 0) { // Dodgy Firefox issue, possibly <bugzilla.mozilla.org/show_bug.cgi?id=488605>
				if(shoutToSend) queuedShout = shoutToSend; // Re-queue the shout that failed to send

				clearTimeout(timeoutR);
				timeoutR = setTimeout(sbRefresh, 1000);
			} else {
				alert('Shoutbox '+submitModeStr+' error: Unexpected status code '+x.status);
				sbHandleQueuedShout();
			}
			loadingNow = false;
			eltOnline.innerHTML = 'Download failed!';
			return;
		}

		var unknownError=true, shoutsUpdated=false;

		if(x.responseXML) { // Will be null if the response was not XML
			var xmlRoot = x.responseXML.documentElement;
			if(xmlRoot.tagName == 'shoutbox') {
				if(submitMode) {
					var xmlStat = xmlRoot.getElementsByTagName('ajaxsubmit').item(0);
					// Currently this attribute should never be anything other than "ok" (error messages aren't returned inside <shoutbox>...</shoutbox>)
					if(xmlStat && xmlStat.getAttribute('status') == 'ok') {
						// Message was accepted by the server, so now it's safe to clear the field
						document.getElementById('message').value = '';
					}
				}

				var newContent = readCDATA(xmlRoot.getElementsByTagName('onlineusers').item(0));
				eltOnline.innerHTML = (newContent) ? newContent : 'Failed to update online users!';

				var newChecksum = '';
				var xmlShouts = xmlRoot.getElementsByTagName('shouts').item(0);

				if(xmlShouts) {
					if(xmlShouts.getAttribute('checksum') !== null) newChecksum = xmlShouts.getAttribute('checksum');
					if(xmlShouts.getAttribute('max_id')) {
						sbTitleNotif.maxSeenShout = xmlShouts.getAttribute('max_id');

						if(sbTitleNotif.windowFocused) sbTitleNotif.maxUserSeenShout = sbTitleNotif.maxSeenShout;
					}
				}

				// Don't replace the shouts if they haven't actually changed since the last refresh
				if(!newChecksum || newChecksum != lastChecksum) {
					lastChecksum = newChecksum;

					if(newContent = readCDATA(xmlShouts)) {
						try {
							eltShouts.innerHTML = newContent;
						} catch(e) {
							// Internet Explorer issue: <http://support.microsoft.com/kb/239832>
							// Need to replace the entire table, as eltShouts.innerHTML is read-only
							// Will this work? Who knows. :zippy:
							eltShouts.parentNode.innerHTML = '<table id="shouts" width="'+eltShouts.getAttribute('width')+'" border="'+eltShouts.getAttribute('border')+'" cellpadding="'+eltShouts.getAttribute('cellpadding')+'" cellspacing="'+eltShouts.getAttribute('cellspacing')+'">'+newContent+'</table>';
							// Make sure this still points to the right thing
							eltShouts = document.getElementById('shouts');
						}
						shoutsUpdated = true;

						if(scrolledToBottom && typeof document.addEventListener == 'function') {
							// Because images loading can change the scroll position, the final scroll to the bottom should be done immediately after all images have loaded.
							// Inefficient; look for better solution.
							var imgElts = eltShouts.getElementsByTagName('img');
							for(var i = 0; i < imgElts.length; i++) {
								imgElts[i].addEventListener('load', function() { scrollTo(0,999999); }, false);
							}
						}
					} else {
						alert('Shoutbox refresh error: Failed to update shouts!');
					}
				}
				newContent = null;
				unknownError = false;

				if(!sbTitleNotif.windowFocused) {
					var xmlNewShouts = xmlRoot.getElementsByTagName('newshouts').item(0);
					if(xmlNewShouts) {
						sbTitleNotif.totalUnseenShouts += parseInt(xmlNewShouts.getAttribute('total'));
						sbTitleNotif.update();
					}
				}

				// Update this in case it changed since the last refresh
				var xmlToken = xmlRoot.getElementsByTagName('token').item(0);
				if(xmlToken && xmlToken.getAttribute('id') == 'validshout' && xmlToken.getAttribute('value') && eltMsgForm) {
					eltMsgForm.elements['validshout'].value = xmlToken.getAttribute('value');
				}
			}
			else if(xmlRoot.tagName == 'response' && xmlRoot.getAttribute('stat') == 'fail') {
				var xmlError = xmlRoot.getElementsByTagName('error').item(0);
				if(xmlError && xmlError.getAttribute('msg')) {
					unknownError = false;
					// To test this, AJAX-submit the shout "/q asdf"
					alert("Shoutbox "+submitModeStr+" error: "+xmlError.getAttribute('msg'));
				}
			}
		}

		if(unknownError) eltOnline.innerHTML = 'Unknown error!';

		if(shoutsUpdated) {
			if(quoteLinkifyWhat) sbLinkifyIds();

			sbLinkifyIgnored();
		}

		// Scroll back to bottom if we were there before refreshing
		if(scrolledToBottom) scrollTo(0,999999);

		loadingNow = false;
		sbHandleQueuedShout();
	};

	x.open(((submitMode) ? 'post' : 'get'), location.href + (location.href.indexOf('?') == -1 ? '?' : '&') + 'format=xml-wrapper', true);

	if(unhiddenShouts.length > 0) {
		// Pass these shout IDs back to the server so they won't be hidden in the response HTML
		x.setRequestHeader('X-Unhidden-Shouts', unhiddenShouts.join(','));
	}

	if(sbTitleNotif.maxSeenShout && !sbTitleNotif.windowFocused) {
		// The server will count the number of new shouts with IDs higher than this one
		x.setRequestHeader('X-Max-Seen-Shout', sbTitleNotif.maxSeenShout);
	}

	if(submitMode) {
		// encodeURIComponent() always returns a UTF-8 encoded string, so tell the server to expect UTF-8
		x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
		x.send('action=send&username='+encodeURIComponent(shoutToSend.username)+'&message='+encodeURIComponent(shoutToSend.message)+'&validshout='+encodeURIComponent(eltMsgForm.elements['validshout'].value)+'&xmlhttp=1&encoding=utf-8');
	} else {
		x.send(null);
	}
}

function sbUpdateThrobber(start, rfEnabled) {
	if(!eltSwitch) return;

	if(start) {
		rfSwitchPosition = 1;

		if(typeof window.sbThrobberTimeout == 'number') {
			// If the throbber is already throbbing (¬¬), make sure it doesn't go crazy
			clearTimeout(window.sbThrobberTimeout);
		}
	} else if(!rfSwitchPosition && rfEnabled) {
		return; // Refresh has been disabled
	}

	var stop = false;

	if(rfSwitchPosition > 7) {
		// Loop back to frame 1 of the animation
		rfSwitchPosition = 1;
		// And stop there?
		if(!loadingNow) {
			stop = true;

			if(!rfEnabled) rfSwitchPosition = 0; // Stop on the "disabled" image
		}
	} else {
		// Advance the frame
		rfSwitchPosition++;
	}

	if(!stop) window.sbThrobberTimeout = setTimeout(function() { sbUpdateThrobber(false, rfEnabled); }, 80);

	eltSwitch.style.backgroundPosition = (rfSwitchPosition * -16)+'px 0px';
}

/**
* This is called when a new shout is queued for sending, and every time the shoutbox is done refreshing.
* Useful in case a user tries to submit a shout while the shoutbox is refreshing.
*/
function sbHandleQueuedShout() {
	if(queuedShout) {
		clearTimeout(timeoutR);
		timeoutR = setTimeout(sbRefresh, 0);
	}
}

function checkName() {
	if(eltMsgForm && typeof(eltMsgForm.elements['username']) != 'undefined') { var eltMessage = eltMsgForm.elements['message']; if(eltMsgForm.elements['username'].value == 'Name' && eltMessage.value && eltMessage.value != eltMessage.defaultValue) { alert('Are you too lazy to type a username?'); return false; } }
	return true;
}

function sbLinkifyIds() {
	var targetClassName = (quoteLinkifyWhat == 'shoutids') ? 'shoutid' : 'timestamp';
	var cnRe = new RegExp('(^|\\s)'+targetClassName+'(\\s|$)');

	var elt, shoutid, spanElts = document.getElementsByTagName('span');
	for(var i=0; i<spanElts.length; i++) {
		elt = spanElts[i];
		if(elt.className.length > 0 && (elt.className == targetClassName || cnRe.test(elt.className))) {
			if(quoteLinkifyWhat == 'shoutids') {
				shoutid = parseInt(elt.innerHTML.replace('[',''));
			} else {
				shoutid = Number.NaN;

				var cellTitle = elt.parentNode.getAttribute('title');
				if(cellTitle) {
					if((cellTitle = cellTitle.match(/, ID (\d+)/))) {
						shoutid = parseInt(cellTitle[1]);
					}
				}
			}
			if(!isNaN(shoutid)) elt.innerHTML = '<a onclick="return sbQuote('+shoutid+')" title="Quote this shout">'+elt.innerHTML+'</a>';
		}
	}
}

function sbLinkifyIgnored() {
	var cnRe = new RegExp('(^|\\s)ignored_placeholder(\\s|$)');
	var elt;
	var divElts = (eltShouts || document).getElementsByTagName('div');
	for(var i=0; i<divElts.length; i++) {
		elt = divElts[i];
		if(elt.className && (elt.className == 'ignored_placeholder' || cnRe.test(elt.className))) {
			elt.innerHTML += " Click here to view it."; // Added dynamically in case user has JS disabled or something else goes awry
			if(typeof document.addEventListener == 'function') {
				elt.addEventListener('click', sbUnhideShout, false);
			} else {
				elt.onclick = sbUnhideShout;
			}
		}
	}
}

function sbUnhideShout(evt) {
	var placeholderId = (evt) ? evt.target.id : window.event.srcElement.id; // IE compat
	var eltPlaceholder = document.getElementById(placeholderId);
	if(!eltPlaceholder) return;

	var match = placeholderId.match(/(\d+)$/);
	var shoutId = match[1];

	// The shout content div is right before the placeholder div; should be easy to find
	var eltShoutContent = eltPlaceholder.previousSibling;
	while(eltShoutContent && eltShoutContent.nodeType != 1) eltShoutContent = eltShoutContent.previousSibling;

	if(eltShoutContent) {
		eltShoutContent.style.display = '';
		eltPlaceholder.style.display  = 'none';
	} else {
		alert("Failed to un-hide ignored shout!");
	}

	unhiddenShouts.push(shoutId);
}

function sbQuote(shoutid) {
	var eltMessage = document.getElementById('message');
	if(!eltMessage) return false;
	clearField(eltMessage); // Clear default value if necessary
	// qf is "quote full" (does not strip out nested quotes)
	var command = (eltMessage.value.indexOf('/q '+shoutid+' ') == 0) ? 'qf' : 'q';
	eltMessage.value = '/'+command+' '+shoutid+' '+eltMessage.value.replace(/^\/(q|qf|q2|quote) \d+( |$)/, '');
	// Place focus back on text field (because clicking the shout id would take it away),
	// and move the cursor to the end
	var len = eltMessage.value.length;
	if(eltMessage.createTextRange) { // IE
		var r = eltMessage.createTextRange();
		r.collapse(true);
		r.moveEnd('character', len);
		r.moveStart('character', len);
		r.select();
	} else {
		eltMessage.focus();
		if(eltMessage.setSelectionRange) { // Opera and Mozilla
			eltMessage.setSelectionRange(len, len);
		}
	}
	return false;
}

function sbUpdateCurrentTime() {
	var eltTime = document.getElementById('currenttime');
	if(!eltTime) return;

	if(timeFormat12 === null) {
		timeFormat12 = (eltTime.innerHTML.indexOf(' ') != -1);
	}

	var d = new Date();
	var nhours = d.getHours();
	var nmins  = d.getMinutes();
	var nsecs  = d.getSeconds();
	if(nmins<10) nmins='0'+nmins;
	if(nsecs<10) nsecs='0'+nsecs;
	if(timeFormat12) {
		// 12-hour format
		var ampm = ' ' + ((nhours>=12)?'PM':'AM');
		if(nhours>12) nhours -= 12;
		if(nhours==0) nhours = 12;
	} else {
		// 24-hour format
		var ampm = '';
		if(nhours<10) nhours='0'+nhours;
	}
	setTimeout(sbUpdateCurrentTime, 1000);
	eltTime.innerHTML = nhours+':'+nmins+':'+nsecs+ampm;
}

//var dbg_isie = (navigator.appName.toLowerCase().indexOf('microsoft') != -1);

var lastChecksum = shoutsChecksum;
var eltMsgForm=null, eltSwitch=null, eltShouts=null, eltOnline=null, unhiddenShouts=new Array(), queuedShout=null, timeFormat12=null, loadingNow=false;

var sbTitleNotif = {
	maxSeenShout: null,
	maxUserSeenShout: null,
	totalUnseenShouts: 0,
	windowFocused: true,

	update: function() {
		if(this.windowFocused) this.totalUnseenShouts = 0;

		document.title = ((this.totalUnseenShouts < 1) ? '' : '('+this.totalUnseenShouts+') ') + document.title.replace(/^\(\d+\)\s+/, '');
	}
};

if(typeof window.addEventListener == 'function') {
	window.addEventListener('blur',  function() { sbTitleNotif.windowFocused = false; }, false);
	window.addEventListener('focus', function() { if(!sbTitleNotif.windowFocused) { sbTitleNotif.windowFocused = true; sbTitleNotif.update(); } }, false);
}


/**
* This function is not yet used.
*
* eltMsgForm.onsubmit = function() { return sbOnSubmitShout(); };
*/
function sbOnSubmitShout() {
	// For guest support
	if(!checkName()) return false;

	// If refreshRate is null, it means we're not on page 1 of the shoutbox.
	// If the refresh method isn't AJAX, submitting shouts via AJAX is kinda silly.
	if(refreshRate == null || refreshMethod == 'formsubmit') {
		// Submit form normally
		return true;
	}

	var eltUsername = document.getElementById('guest_username');
	var username = (eltUsername) ? eltUsername.value : '';

	var message = document.getElementById('message').value;

	if(message == '') {
		// Blank message, maybe user just wanted to refresh?
		return true;
	}

	if(message.match(/^\s*\/\w+/) && !message.match(/^\s*\/(text|me|slap|quote|q[f2]?)\s/)) {
		// It looks like a command, so allow it to be submitted normally
		return true;
	}

	queuedShout = {'username': username, 'message': message}; // sbRefresh() will check this variable and take care of submitting the shout
	sbHandleQueuedShout();

	return false; // Block the form submission
}
