/*======================================================================*\
|| #################################################################### ||
|| # ---------------------------------------------------------------- # ||
|| # Copyright ©2007-2009 Fillip Hannisdal AKA Revan/NeoRevan/Belazor # ||
|| # All Rights Reserved. 											  # ||
|| # This file may not be redistributed in whole or significant part. # ||
|| # ---------------------------------------------------------------- # ||
|| # You are not allowed to use this on your server unless the files  # ||
|| # you downloaded were done so with permission.					  # ||
|| # ---------------------------------------------------------------- # ||
|| #################################################################### ||
\*======================================================================*/

// #######################################################################
// ######################## START MAIN SCRIPT ############################
// #######################################################################

// #############################################################################
// Setup the main object
vBShout_Obj = function()
{
	// Object variables
	this.ajax			= new Array();	// Array of AJAX objects
	this.frames			= new Array();	// A list of all the frames we interact with
	this.timers			= new Array();	// A list of all the timers we keep track of
	this.lookup			= new Array();	// A list of all the users we have PMed at some point (userid - username)
	this.smilies;						// Captain Placeholder of the Smiley Popup Functions Brigade
	this.fonts;							// Captain Placeholder of the Font Dropdown Functions Brigade
	this.colors;						// Captain Placeholder of the Color Dropdown Functions Brigade
	this.fontstyles;					// Captain Placeholder of the Bold/Italic/Underline Button Functions Brigade
	this.editor 		= {				// A list of all editor styling we may apply
		'bold' 		: '',
		'italic'	: '',
		'underline'	: '',
		'color'		: 'Black',
		'font'		: 'Tahoma',
		'size'		: '11px'
	};
	this.tabs			= {				// A list of all the tabs we are playing with
		'shouts'		: true,
		'activeusers'	: true
	};	
	
	// Integer variables
	this.floodcheck		= 3;			// How big a delay (in seconds) between shouts
	this.refreshtime	= 5;			// How often (in seconds) to auto-refresh
	this.idlelimit		= 180;			// A counter for how long we've been idle
	this.idletime		= 0;			// A counter for how long we've been idle
	this.scrollpos		= 0;			// Helper to determine whether the scrollbar has moved
	this.maxlength		= 256;			// The maximum length of a shout
	this.shoutid		= 0;			// Set this to > 0 when editing a shout
	this.aoptime		= 0;			// Set this to when we last fetched from the database
	this.lastshout		= 0;			// Set this to when we last shouted
	this.pmuserid		= 0;			// Userid of the person we are PMing
	
	// Boolean variables
	this.aop			= false;		// Whether we're using the optimisation protocol
	this.detached		= false;		// Whether we're viewing the shoutbox in detached mode
	this.idle			= false;		// Whether we're idle at all
	
	// String variables
	this.shoutorder		= 'ASC';		// The direction in which we're shouting (DESC = Newest First - ASC = Oldest First)
	this.editorid		= 'dbtech_shoutbox_editor';		// ID of the editor
	this.tab			= 'shouts';		// Where we are by default


	// #########################################################################
	// Initialise the Shoutbox
	this.init = function()
	{
		if (!AJAX_Compatible)
		{
			// AJAX won't work, this is foar srs.
			this.set_message('Either your browser does not support AJAX, or the forum administrator has chosen to disable AJAX. vBShout cannot run under these conditions.', 'error');
			// REPLACESTRING
			
			return false;
		}
		
		// Set our frames
		this.frames['shoutframe'] 	= YAHOO.util.Dom.get('dbtech_shoutbox_frame');			// The surrounding master frame
		this.frames['content'] 		= YAHOO.util.Dom.get('dbtech_shoutbox_content');		// The main shoutbox area
		this.frames['editor'] 		= YAHOO.util.Dom.get(this.editorid);					// The shoutbox editor
		this.frames['tabs'] 		= YAHOO.util.Dom.get('dbtech_shoutbox_tabs');			// The shoutbox tabs
		this.frames['activeusers']	= YAHOO.util.Dom.get('dbtech_shoutbox_activeusers');	// The Active Users count
		this.frames['charcount']	= YAHOO.util.Dom.get('dbtech_shoutbox_remainingchars');	// The Remaining Characters count
		
		// Initialise any enabled editor tools
		this.init_editor_tools();
		
		if (this.frames['editor'])
		{
			// Check the input length
			this.check_length();
		}
		
		// Finally fetch the shouts
		this.fetch(this.tab, true);
		
		if (this.idle)
		{
			// We should be flagged as idle
			this.flag_idle();
		}
		else
		{
			// Schedule idle check
			this.check_idle();
			
			// Schedule fetching shouts 
			this.schedule_refresh();
		}
		
		// Store the phrased variable
		var everyone 	= YAHOO.util.Dom.get('dbtech_shoutbox_everyone');
		if (everyone)
		{
			this.everyone 	= everyone.innerHTML;
		}
	}
	
	// #########################################################################	
	// Shorthand to schedule a refresh of the shouts
	this.schedule_refresh = function()
	{
		// Log a debug message to the console
		console.log(this.timestamp() + "Scheduling fetching current tab every %s seconds", this.refreshtime);
		
		// Clear the shout refresh interval
		clearInterval(this.timers['tab']);
		
		// Setup the schedule
		this.timers['tab'] = setInterval("vBShout.fetch();", (this.refreshtime * 1000));		
	}
	
	// #########################################################################	
	// Initialises various editor tools
	this.init_editor_tools = function()
	{
		if (typeof vBShout_Smilies_Obj != 'undefined')
		{
			// Initialise the smilies reference
			this.smilies = new vBShout_Smilies_Obj();
			
			// Set the editor id
			this.smilies.init(this.editorid);
		}
		
		if (typeof vBShout_Fonts_Obj != 'undefined')
		{
			// Intialise the fonts reference
			this.fonts = new vBShout_Fonts_Obj();
			
			// Begin hacking the fonts dropdown
			this.fonts.init('dbtech_shoutbox_editor_wrapper', this.editor['font']);
		}
		
		if (typeof vBShout_Sizes_Obj != 'undefined')
		{
			// Intialise the fonts reference
			this.sizes = new vBShout_Sizes_Obj();
			
			// Begin hacking the sizes dropdown
			this.sizes.init('dbtech_shoutbox_editor_wrapper', this.editor['size']);
		}
		
		if (typeof vBShout_Colors_Obj != 'undefined')
		{
			// Intialise the colors reference
			this.colors = new vBShout_Colors_Obj();
			
			// Begin hacking the colors dropdown
			this.colors.init('dbtech_shoutbox_editor_wrapper', this.editor['color']);
		}
		
		if (typeof vBShout_FontStyle_Obj != 'undefined')
		{
			// Intialise the font styles reference
			this.fontstyles = new vBShout_FontStyle_Obj();
			
			// Begin hacking the font styles buttons
			this.fontstyles.init('dbtech_shoutbox_editor_wrapper', this.editor['bold'], this.editor['italic'], this.editor['underline']);
		}
	}
	
	// #########################################################################	
	// Updates the style properties based on the Editor Tools selections
	this.update_style_property = function(type, value, noupdate)
	{
		// Default to the same property as the style type
		var property 	= type;
		var setValue	= value;
		switch (type)
		{
			case 'bold':
				property 	= 'fontWeight';
				setValue = (value ? type : '');
				break;
				
			case 'italic':
				property 	= 'fontStyle';
				setValue = (value ? type : '');
				break;
				
			case 'underline':
				property 	= 'textDecoration';
				setValue = (value ? type : '');
				break;
				
			case 'font':
				property 	= 'fontFamily';
				break;
				
			case 'size':
				property 	= 'fontSize';
				switch (value)
				{
					case '1':
					case '<font size="1">1</font>':
						setValue = '9px';
						value = setValue;
						break;
						
					case '2':
					case '<font size="2">2</font>':
						setValue = '11px';
						value = setValue;
						break;
						
					case '3':
					case '<font size="3">3</font>':	
						setValue = '13px';
						value = setValue;
						break;
						
					case '4':
					case '<font size="4">4</font>':	
						setValue = '15px';
						value = setValue;
						break;
						
					case '5':
					case '<font size="5">5</font>':	
						setValue = '17px';
						value = setValue;
						break;
						
					case '6':
					case '<font size="6">6</font>':	
						setValue = '19px';
						value = setValue;
						break;
						
					case '7':
					case '<font size="7">7</font>':	
						setValue = '21px';
						value = setValue;
						break;
				}
				break;
		}
		
		// Log a debug message to the console
		console.log(this.timestamp() + "Style property %s set. Value: %s.", type, setValue);
		
		if (type == 'size')
		{
			// Set the editor style
			eval("this.frames['shoutframe'].style." + property + " = '" + setValue + "';");
		}
		else
		{
			// Set the editor style
			eval("this.frames['editor'].style." + property + " = '" + setValue + "';");
		}
		
		if (this.editor[type] != value && !noupdate)
		{
			// Log a debug message to the console
			console.log(this.timestamp() + "Style property %s changed. Old value: %s - New value: %s", type, this.editor[type], value);
			
			// Change the editor type
			this.editor[type] = value;
			
			// Save the style properties
			this.save_editor_styles();
		}
	}
	
	// #########################################################################	
	// Clear the contents of the editor
	this.clear = function()
	{
		// Log a debug message to the console
		console.log(this.timestamp() + "Removing contents of the editor.");
		
		// Clear the contents of the editor
		this.frames['editor'].value = '';
		this.check_length();
	}

	// #########################################################################	
	// Cancels editing a shout
	this.cancel_shout_editing = function()
	{
		// Remove all evidence of editing a shout
		this.shoutid = 0;
		YAHOO.util.Dom.get('dbtech_shoutbox_deletebutton').style.display = 'none';
		
		// Help text change
		YAHOO.util.Dom.get('dbtech_shoutbox_target').style.display = 'inline';
		YAHOO.util.Dom.get('dbtech_shoutbox_editing').style.display = 'none';
		
		// Clear the editor
		this.clear();
	}
	
	// #########################################################################	
	// Function to begin PMing an user
	this.create_pm = function(userid, username)
	{
		if (!this.lookup[userid])
		{
			// Add the username to the lookup
			this.lookup[userid] = username;
		}
		
		// Creates a new PM to a given user
		this.add_tab('pm_' + userid, username, true, "show_pm_tab('" + userid + "')", "close_pm_tab(this, '" + userid + "')");
		
		// Shows the PM tab
		this.show_pm_tab(userid);
		
		return false;
	}
	
	// #########################################################################	
	// Function to display a PM tab
	this.show_pm_tab = function(userid)
	{
		if (this.tab == 'pm_' + userid)
		{
			// We're already viewing this PM tab.
			return false;
		}
		
		// Blank this out just in case we have no PMs
		this.frames['content'].innerHTML = '';
		
		// Show the tab
		this.show_tab('pm_' + userid, true);
		
		// Set new PM User
		this.pmuserid = userid;
		
		// Update who we're shouting to
		this.set_shout_target(this.lookup[userid]);
		
		return false;
	}
	
	// #########################################################################	
	// Function to close a PM tab	
	this.close_pm_tab = function(tabobj, userid)
	{
		if (this.tab == 'pm_' + userid)
		{
			// We were viewing this PM tab.
			this.pmuserid = 0;
			this.set_shout_target(false);
		}
		
		// Close the tab
		this.close_tab(tabobj, 'pm_' + userid);
		
		return false;
	}
	
	// #########################################################################	
	// Adds a tab
	this.add_tab = function(tabid, text, canclose, showfunc, closefunc)
	{
		// Ensure these are not nil
		showfunc 	= showfunc 	? showfunc 	: "show_tab('" + tabid + "')";
		closefunc 	= closefunc ? closefunc : "close_tab(this, '" + tabid + "')";
		
		if (this.tabs[tabid])
		{
			// Show this tab instead
			eval('this.' + showfunc + ';');
		}
		
		// This tab now exists
		this.tabs[tabid] = true;
		
		var html = '';
		if (canclose)
		{
			// We can close the tab
			html += ' [<a href="javascript://" onclick="return vBShout.' + closefunc + ';">X</a>]';
		}
		
		// Create the tab
		cellposition = this.frames['tabs'].rows[0].cells.length - 1;
		newtab = this.frames['tabs'].rows[0].insertCell(cellposition);
		newtab.className = 'dbtech_vbshout_tabcontainer';
		newtab.innerHTML = '<div id="' + tabid + '" class="dbtech_vbshout_tabs alt"><a href="javascript://" onclick="return vBShout.' + showfunc + ';">' + text + '</a>' + html + '</div>';
	}
	
	// #########################################################################	
	// Displays the contents of a tab
	this.show_tab = function(tabid, force)
	{
		if (this.tab != tabid)
		{
			// Log a debug message to the console
			console.log(this.timestamp() + "Switching from %s to %s.", this.tab, tabid);		
		
			if (this.tab.indexOf('pm', 0) > -1)
			{
				// Reset PM info
				this.pmuserid = 0;
				this.set_shout_target(false);
			}
			
			// Set tab styles
			YAHOO.util.Dom.get(this.tab).className = 'dbtech_vbshout_tabs alt2';
			YAHOO.util.Dom.get(tabid).className = 'dbtech_vbshout_tabs alt';
			
			// Set the new content
			this.tab = tabid;
			
			// We're changing shit, so force
			force = true;
		}
		
		if (force)
		{
			// We're needing new content
			this.fetch(tabid, true);
		}
		
		return false;
	}
	
	// #########################################################################	
	// Closes a tab
	this.close_tab = function(tabobj, tabid)
	{
		// This tab no longer exists
		this.tabs[tabid] = false;
		
		// Removes the tab object
		tabobj.parentNode.parentNode.parentNode.removeChild(tabobj.parentNode.parentNode);
		
		if (this.tab == tabid)
		{
			// We revert to fetching shouts
			this.tab = 'shouts';
			
			// Set tab styles
			YAHOO.util.Dom.get(this.tab).className = 'dbtech_vbshout_tabs alt';
			
			 // Re-fetches shouts
			this.fetch('shouts', true);
		}
		
		// Log a debug message to the console
		console.log(this.timestamp() + "Switching from %s to %s.", tabid, this.tab);		
		
		return false;
	}
	
	// #########################################################################	
	// Sets the shout target
	this.set_shout_target = function(username)
	{
		// Fetch the two objects
		var everyone 	= YAHOO.util.Dom.get('dbtech_shoutbox_everyone');
		var user 		= YAHOO.util.Dom.get('dbtech_shoutbox_pmuser');
		
		if (everyone)
		{
			if (!username)
			{
				// Reverting to everyone
				everyone.innerHTML		= this.everyone;
			}
			else
			{
				// Setting an username
				everyone.innerHTML 		= PHP.trim(username);
			}
		}
	}
	
	// #########################################################################	
	// Repeating function to check if we're idle
	this.check_idle = function()
	{
		if (this.idlelimit == 0 || this.idle)
		{
			// We're not even checking for idle or we're already idle
			return false;
		}
		
		// Log the error to the console
		console.log(this.timestamp() + "Checking for idle... Have been idle for %s seconds. Limit: %s seconds.", this.idletime, this.idlelimit);		
		
		if (this.idletime >= this.idlelimit)
		{
			// Yep, we're idle
			this.idle = true;
			
			// Clear the shout refresh interval
			clearInterval(this.timers['tab']);			
			
			// We're idle
			this.flag_idle();
			
			// No need to increment it further
			return false;
		}
		
		// Check again in 1 sec
		setTimeout("vBShout.check_idle()", 1000);
				
		// Increment the time we've been idle
		this.idletime++;
	}
	
	// #########################################################################	
	// Shorthand function to flag as idle
	this.flag_idle = function()
	{
		this.set_message('You are currently showing up as idle. Click <a href="javascript://" onclick="return vBShout.unidle();">here</a> to resume activity.', 'notice');
		// REPLACESTRING		
	}
	
	// #########################################################################
	// We're no longer idle
	this.unidle = function()
	{
		// Log the error to the console
		console.log(this.timestamp() + 'Removing idle...');		
		
		// We're no longer idle
		this.idletime 	= 1;
		this.idle 		= false;
		
		// Hide the error message
		this.hide_message('notice');
		
		// Begin the timer again
		setTimeout("vBShout.check_idle()", 1000);
		
		// We're no longer idle, instantly fetch shouts unless we're posting
		this.fetch(this.tab);
		
		// Begin scheduling refreshes of the current tab
		this.schedule_refresh();
	}
	
	
	// #######################################################################
	// ######################## AJAX FUNCTIONS ###############################
	// #######################################################################
	
	// #########################################################################
	// Fetches a given type of content
	this.fetch = function(type, force)
	{
		if (!type)
		{
			// Avoid refreshing problem
			type = this.tab;
		}
		
		if ((!YAHOO.util.Connect.isCallInProgress(this.ajax['posting']) && !this.idle) || force == true)
		{
			// Initialise this
			var extraparams = '';
			
			if (type == 'shouts' && !force && this.aop)
			{
				// Override type since we're not forcing shouts
				type = 'aop';
			}
			
			// Log a debug message to the console
			console.log(this.timestamp() + 'Fetching ' + type + '...');			
			
			switch (type)
			{
				case 'aop':
					extraparams += '&aoptime=' + this.aoptime;
					break;
					
				case 'shout':
					extraparams += '&shoutid=' + this.shoutid;
					break;
			}
			
			if (this.detached)
			{
				// We're in detached mode
				extraparams += '&detached=1';
			}
			
			// Set shout order
			extraparams += '&shoutorder=' + this.shoutorder;
			
			// Execute AJAX
			YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_fetch', {
				success: this.ajax_completed,
				failure: this.handle_ajax_error,
				timeout: vB_Default_Timeout,
				scope: this
			}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_fetch&type=' + PHP.urlencode(type) + extraparams);
		}
	}
	
		
	// #########################################################################	
	// Submit a new shout or save the editing shout
	this.save = function()
	{
		if (this.frames['editor'].value.length == 0)
		{
			// We don't care about empty shouts
			return false;
		}
		
		// Initialise this
		var extraparams = '';
		
		// Set the current time
		var timenow = new Date().getTime();
		
		if ((timenow - this.lastshout) < (this.floodcheck * 1000) && this.lastshout > 0)
		{
			// Flood check
			this.set_message('You must wait at least ' + this.floodcheck + ' seconds between shouts. It has been ' + parseInt((timenow - this.lastshout) / 1000)  + ' seconds since your last shout.', 'error');
			return false;
		}
		
		// Set our last shout time
		this.lastshout = timenow;
		
 		if (this.shoutid > 0)
		{
			// We're editing a shout
			console.log(this.timestamp() + 'Attempting to save shout: %s...', this.shoutid);			
			
			// Add some extra parameters
			extraparams += '&shoutid=' + parseInt(this.shoutid);
			
			if (this.pmuserid)
			{
				// Set who we're PMing
				extraparams += '&type=pm&userid=' + this.pmuserid;
			}			
		}
		else
		{
			if (this.pmuserid)
			{
				// We're submitting a new PM
				console.log(this.timestamp() + 'Attempting to PM user: %s', this.lookup[this.pmuserid]);				
				
				// Set who we're PMing
				extraparams += '&type=pm&userid=' + this.pmuserid;
			}
			else
			{
				// We're submitting a new shout
				console.log(this.timestamp() + 'Attempting to insert shout...');				
			}
		}
		
		if (this.detached)
		{
			// We're in detached mode
			extraparams += '&detached=1';
		}		
	
		// Execute AJAX
		this.ajax['posting'] = YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_save', {
			success: this.ajax_completed,
			failure: this.handle_ajax_error,
			timeout: vB_Default_Timeout,
			scope: this
		}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_save&message=' + PHP.urlencode(PHP.trim(this.frames['editor'].value)) + extraparams);

		if (this.idle)
		{
			// We're obviously no longer idle, but init AJAX first so that we don't double fetch shouts
			this.unidle();
		}
		
		if (this.shoutid)
		{
			// We're no longer editing a shout
			this.cancel_shout_editing();			
		}
		
		return false;
	}
	
	// #########################################################################	
	// Begins editing a shout
	this.edit_shout = function(shoutid)
	{
		// Set the shout id
		this.shoutid = shoutid;			
		YAHOO.util.Dom.get('dbtech_shoutbox_deletebutton').style.display = 'inline';
		
		// Help text change
		YAHOO.util.Dom.get('dbtech_shoutbox_target').style.display = 'none';
		YAHOO.util.Dom.get('dbtech_shoutbox_editing').style.display = 'inline';
		
		// Set the editor contents
		this.frames['editor'].value = PHP.unhtmlspecialchars(YAHOO.util.Dom.get('dbtech_shoutbox_shout' + shoutid + '_message').innerHTML);			
		
		// Fetch the shout
		//this.fetch('shout', true);		
		
		// Just to be safe
		this.check_length();
	}	
	
	// #########################################################################	
	// Begins editing a shout
	this.edit_archive_shout = function(shoutid)
	{
		// We're editing a shout
		console.log(this.timestamp() + 'Attempting to edit Archive shout: %s...', shoutid);			
				
		var editor 	= YAHOO.util.Dom.get('dbtech_shoutbox_editor_' + shoutid);
		var message = YAHOO.util.Dom.get('dbtech_shoutbox_message_' + shoutid);
		
		editor.style.display 	= 'block';
		message.style.display 	= 'none';
		
		YAHOO.util.Dom.get('dbtech_shoutbox_editor_text_' + shoutid).value = PHP.unhtmlspecialchars(YAHOO.util.Dom.get('dbtech_shoutbox_message_raw_' + shoutid).innerHTML);
	}
	
	// #########################################################################	
	// Begins editing a shout
	this.cancel_editing_archive_shout = function(shoutid)
	{
		// We're editing a shout
		console.log(this.timestamp() + 'Cancelling editing Archive shout: %s...', shoutid);			
				
		var editor 	= YAHOO.util.Dom.get('dbtech_shoutbox_editor_' + shoutid);
		var message = YAHOO.util.Dom.get('dbtech_shoutbox_message_' + shoutid);
		
		editor.style.display 	= 'none';
		message.style.display 	= 'block';
		
		YAHOO.util.Dom.get('dbtech_shoutbox_editor_text_' + shoutid).value = PHP.unhtmlspecialchars(YAHOO.util.Dom.get('dbtech_shoutbox_message_raw_' + shoutid).innerHTML);
	}
	
	// #########################################################################
	// Save the editor styles
	this.delete_archive_shout = function(shoutid)
	{
		if (confirm('Are you sure you wish to delete this shout?'))
		{
			// Remove the shout
			YAHOO.util.Dom.get('dbtech_shoutbox_shout' + shoutid).style.display = 'none';
			
			// We're editing a shout
			console.log(this.timestamp() + 'Attempting to delete Archive shout: %s...', shoutid);			
			
			// Execute AJAX
			YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_delete', {
				success: this.ajax_completed,
				failure: this.handle_ajax_error,
				timeout: vB_Default_Timeout,
				scope: this
			}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_delete&shoutid=' + shoutid + '&source=archive');
		}
		
		return false;
	}	
	
	// #########################################################################
	// Save the editor styles
	this.save_archive_shout = function(shoutid)
	{
		// We're editing a shout
		console.log(this.timestamp() + 'Attempting to save Archive shout: %s...', shoutid);			
		
		// Set the raw text
		YAHOO.util.Dom.get('dbtech_shoutbox_message_raw_' + shoutid).innerHTML = YAHOO.util.Dom.get('dbtech_shoutbox_editor_text_' + shoutid).value;
		
		var editor 	= YAHOO.util.Dom.get('dbtech_shoutbox_editor_' + shoutid);
		var message = YAHOO.util.Dom.get('dbtech_shoutbox_message_' + shoutid);
		
		editor.style.display 	= 'none';
		message.style.display 	= 'block';
		
		// Execute AJAX
		YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_save', {
			success: this.ajax_completed,
			failure: this.handle_ajax_error,
			timeout: vB_Default_Timeout,
			scope: this
		}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_save&shoutid=' + shoutid + '&message=' + PHP.urlencode(PHP.trim(YAHOO.util.Dom.get('dbtech_shoutbox_editor_text_' + shoutid).value)) + '&source=archive');
	}	
	
	// #########################################################################
	// Save the editor styles
	this.save_editor_styles = function()
	{
		var styleproperties = new Array(
			'editor[bold]=' + (this.editor['bold'] ? 1 : 0),
			'editor[italic]=' + (this.editor['italic'] ? 1 : 0),
			'editor[underline]=' + (this.editor['underline'] ? 1 : 0),
			'editor[color]=' + this.editor['color'],
			'editor[font]=' + this.editor['font'],
			'editor[size]=' + this.editor['size']
		);
		
		var extraparams = '';
		
		if (this.detached)
		{
			// We're in detached mode
			extraparams += '&detached=1';
		}		
		
		// Execute AJAX
		YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_styleprops', {
			success: this.ajax_completed,
			failure: this.handle_ajax_error,
			timeout: vB_Default_Timeout,
			scope: this
		}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_styleprops&' + styleproperties.join('&') + extraparams);
		
		if (this.idle)
		{
			// We're obviously no longer idle, but init AJAX first so that we don't double fetch shouts
			this.unidle();
		}		
	}
	
	// #########################################################################
	// Save the editor styles
	this.delete_shout = function()
	{
		// Initialise this
		var extraparams = '';
		
 		if (this.shoutid == 0)
		{
			// This should never happen but just in case
			return false;
		}
		
		// We're editing a shout
		console.log(this.timestamp() + 'Attempting to delete shout: %s...', this.shoutid);			
		
		if (this.pmuserid)
		{
			// Set who we're PMing
			extraparams += '&type=pm&userid=' + this.pmuserid;
		}	
		
		if (this.detached)
		{
			// We're in detached mode
			extraparams += '&detached=1';
		}		
		
		// Execute AJAX
		YAHOO.util.Connect.asyncRequest('POST', 'ajax.php?do=dbtech_vbshout_delete', {
			success: this.ajax_completed,
			failure: this.handle_ajax_error,
			timeout: vB_Default_Timeout,
			scope: this
		}, SESSIONURL + 'securitytoken=' + SECURITYTOKEN + '&do=dbtech_vbshout_delete&shoutid=' + this.shoutid + extraparams);
		
		// We're no longer editing a shout
		this.cancel_shout_editing();
		
		if (this.idle)
		{
			// We're obviously no longer idle, but init AJAX first so that we don't double fetch shouts
			this.unidle();
		}		
	}
	
	// #########################################################################
	// Finalise fetching of content
	this.ajax_completed = function(ajax)
	{
		if (!ajax.responseXML)
		{
			// Empty response
			this.set_message('Invalid response from server: ' + ajax.responseText, 'error');
			return false;
		}
		
		// check for error first
		var error = ajax.responseXML.getElementsByTagName('error');
		var clear = ajax.responseXML.getElementsByTagName('clear');
			
		if (clear.length)
		{
			// We need to clear an editor
			this.frames[clear[0].firstChild.nodeValue].value = '';
			this.check_length(); // Just to be safe
		}
			
		if (error.length)
		{
			// Throw the error returned
			this.throw_ajax_error(error[0].firstChild.nodeValue);
		}
		else
		{
			// All possible tags
			var activeusers = ajax.responseXML.getElementsByTagName('activeusers');
			var aoptime 	= ajax.responseXML.getElementsByTagName('aoptime');
			var shouts 		= ajax.responseXML.getElementsByTagName('shout');
			var success 	= ajax.responseXML.getElementsByTagName('success');
			var sticky 		= ajax.responseXML.getElementsByTagName('sticky');
			var editor 		= ajax.responseXML.getElementsByTagName('editor');
			var content 	= ajax.responseXML.getElementsByTagName('content');
			var shoutid 	= ajax.responseXML.getElementsByTagName('shoutid');
			var archive 	= ajax.responseXML.getElementsByTagName('archive');
			
			if (success.length)
			{
				// Print success message
				this.set_message(success[0].firstChild.nodeValue, 'success');
			}
			
			if (sticky.length)
			{
				if (!sticky[0].firstChild)
				{
					// We're removing the sticky note
					this.hide_message('sticky');
				}
				else
				{
					// Print sticky message
					this.set_message(sticky[0].firstChild.nodeValue, 'sticky');
				}
			}
			
			if (activeusers.length)
			{
				// Set the active users
				this.frames['activeusers'].innerHTML = activeusers[0].firstChild.nodeValue;
			}
			
			if (aoptime.length)
			{
				// We had an AOP tag
				this.aoptime = aoptime[0].firstChild.nodeValue;
			}
			
			if (editor.length)
			{
				// We're setting the editor contents
				this.frames['editor'].value = editor[0].firstChild.nodeValue;
				this.check_length();				
			}
			
			if (content.length)
			{
				// We're setting shout frame contents
				this.frames['content'].innerHTML = content[0].firstChild.nodeValue;
			}
			
			if (archive.length && shoutid.length)
			{
				// We're setting shout frame contents
				YAHOO.util.Dom.get('dbtech_shoutbox_message_' + shoutid[0].firstChild.nodeValue).innerHTML = archive[0].firstChild.nodeValue;
			}
			
			if (shouts.length)
			{
				// Begin storing the output
				var html = new Array();
				
				for (var i = 0; i < shouts.length; i++)
				{
					// Shorthand
					var shout 		= shouts[i];
					var message 	= PHP.trim(shout.firstChild.nodeValue);
					
					// Set the shout message
					html[i] = message;
				}
				
				if (html.length)
				{
					// Set the content if we had any
					this.frames['content'].innerHTML = html.join('');
				}
				
				if (this.shoutorder == 'ASC' && this.frames['shoutframe'].scrollTop < this.frames['shoutframe'].scrollHeight)
				{
					// Make it snap the scrollbar to the bottom
					if (typeof(HTMLElement) != 'undefined')
					{
						this.frames['shoutframe'].scrollBottom();
					}
					else
					{
						this.frames['shoutframe'].scrollTop = this.frames['shoutframe'].scrollHeight;
					}
				}
			}
		}
	}
	
	
	// #######################################################################
	// ######################## ERROR HANDLING ###############################
	// #######################################################################
	
	// #########################################################################
	// Function for manually throwing an AJAX error
	this.throw_ajax_error = function(errormsg)
	{
		if (YAHOO.util.Dom.get('dbtech_shoutbox_error_message'))
		{
			// Throw the error returned
			this.set_message(errormsg, 'error');
		}
		else
		{
			alert(errormsg);
		}
		
		// Log the error to the console
		console.error(this.timestamp() + "AJAX Error: %s", errormsg);		
	}
	
	// #########################################################################
	// This should never happen.
	this.handle_ajax_error = function(ajax)
	{
		if (YAHOO.util.Dom.get('dbtech_shoutbox_error_message'))
		{
			// Throw the error returned
			this.set_message(ajax.statusText, 'error');
			
			// Log the error to the console
			console.error(this.timestamp() + "AJAX Error: Status = %s: %s", ajax.status, ajax.statusText);
		}
		else
		{
			// Just pop it up
			alert(ajax.statusText);
		}
	}
	
	// #########################################################################
	// Function for displaying a thrown error
	this.set_message = function(msg, type)
	{
		// Log the error to the console
		console.log(this.timestamp() + "Setting %s: %s", type.charAt(0).toUpperCase() + '' + type.substr(1), msg);
		
		if (type != 'sticky' && type != 'notice')
		{
			// Clear any timeouts we may have already
			clearTimeout(this.timers[type]);
			
			// Create a new timer for hiding the frame
			this.timers[type] = setTimeout("vBShout.hide_message('" + type + "')", 5000);
		}
		
		// Display the message
		YAHOO.util.Dom.get('dbtech_shoutbox_' + type + '_message').innerHTML 	= msg;
		YAHOO.util.Dom.get('dbtech_shoutbox_' + type + '_frame').style.display 	= 'block';
		
		if (this.shoutorder == 'ASC' && this.frames['shoutframe'].scrollTop == this.scrollpos)
		{
			if (typeof(HTMLElement) != 'undefined')
			{
				// Scroller hasn't moved, snap to bottom
				this.frames['shoutframe'].scrollBottom();
			}
		}
	}
	
	// #########################################################################
	// Function for displaying a thrown error
	this.hide_message = function(type)
	{
		// Log the error to the console
		console.log(this.timestamp() + "Hiding %s...", type.charAt(0).toUpperCase() + '' + type.substr(1));
		
		// Hide the frame
		YAHOO.util.Dom.get('dbtech_shoutbox_' + type + '_message').innerHTML 	= 'N/A';
		YAHOO.util.Dom.get('dbtech_shoutbox_' + type + '_frame').style.display 	= 'none';
		
		if (this.shoutorder == 'ASC')
		{
			// Store the position of the scroller
			this.scrollpos = this.frames['shoutframe'].scrollTop;
		}
	}
	
	
	// #######################################################################
	// ######################## GENERIC FUNCTIONALITY ########################
	// #######################################################################

	// #########################################################################
	// Process keystroke
	this.keystroke = function(e)
	{
		// What key we pressed
		var keynum = 0;
		
		if (window.event)
		{
			// IE
			keynum = e.keyCode;
		}
		else if (e.which)
		{
			// Netscape / Firefox / Opera
			keynum = e.which;
		}
		
		if (keynum == 27 && this.shoutid)
		{
			// We're editing a shout, cancel editing
			this.cancel_shout_editing();
		}
		
		if (keynum == 13)
		{
			// This was saving the form
			return this.save();
		}
	}
	
	// #########################################################################
	// Check the length of the input
	this.check_length = function()
	{
		if (this.maxlength == 0)
		{
			// We're not checking length
			return true;
		}
		
		if (this.frames['editor'].value.length > this.maxlength)
		{
			// Strip characters that go beyond the limit
			this.frames['editor'].value = this.frames['editor'].value.substring(0, this.maxlength);
		}
		
		// Set the Remaining Characters count
		this.frames['charcount'].innerHTML = (this.maxlength - this.frames['editor'].value.length);		
	}
	
	// #########################################################################
	// Debugging function, generates a timestamp of when something occurred
	this.timestamp = function()
	{
		var d = new Date();
		
		return '[' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + '] ';
	}
};


// #######################################################################
// ######################## LANGUAGE EXTENSIONS ##########################
// #######################################################################

// #############################################################################
// Code to snap a scrollbar to the bottom

if (typeof(HTMLElement) != 'undefined')
{
	HTMLElement.prototype.scrollBottom = function()
	{
		// Finally set the scrollTop attribute
		this.scrollTop = this.scrollHeight;
	}
}
/*======================================================================*\
|| #################################################################### ||
|| # Created: 23:33, Mon Dec 28th 2009								  # ||
|| # SVN: $Rev$							 							  # ||
|| #################################################################### ||
\*======================================================================*/