View Single Post
Old 02-23-2006, 07:17 AM   #1 (permalink)
l33t_1-1axx0r
Huzzah!
 
l33t_1-1axx0r's Avatar
 
Join Date: Feb 2006
Posts: 8
l33t_1-1axx0r is on a distinguished road
Send a message via AIM to l33t_1-1axx0r Send a message via MSN to l33t_1-1axx0r Send a message via Yahoo to l33t_1-1axx0r
AJAX n00b (Pretty involved issue, here!)

Hey guys,

Love the idea of these boards, first of all. Look forward to really getting into them as I'm always looking for ways to improve my own coding while trying to help others. I have some AJAX stuff here that I haven't quite grocked and would love to hear opinions on.

Okay, so maybe some of you vBulletin fans know about the AJAX Shoutbox

Pretty solid piece of coding....for a small forum. When you scale it upwards, however, it becomes increasingly buggy and inefficient, if not just draining on server resources. I have a number of ways I'm trying to improve the efficiency (i.e. adding a seperate column in the DB for tallying shouts as opposed to counting all rows everytime) but one particular issue escapes me and it's mainly due to my complete inexperience with AJAX and Javascript. Trying to learn both, but it's a slow process.

So here are some code snippets
(You can see a shoutbox implementation here: AMN Forums)

Shouting is a simple interactive with a form defined as follows:

Code:
<form action="vbshout.php?{$session['sessionurl']}" method="post" name="vbshoutform" onsubmit="return postShout(this)">
<input type='hidden' name='do' value='shout' />
<input type='hidden' name='color' />
<input type='hidden' name='fontFamily' />
<input type='hidden' name='fontWeight' />
<input type='hidden' name='textDecoration' />
<input type='hidden' name='fontStyle' />
And the AJAX/Javascript functions are here...

Code:
<script type='text/javascript'>
<!--

postingShout = false

function requestShouts()
{
	if (!postingShout)
	{
		ShoutRequest = new vB_AJAX_Handler(true)
		ShoutRequest.onreadystatechange(showShouts)
		ShoutRequest.send('vbshout.php', 'nocache=' + (5 * Math.random() * 1.33) )
	}
}

function showShouts()
{
	if (ShoutRequest)
	{
		if (ShoutRequest.handler.readyState == 4 && ShoutRequest.handler.status == 200 && ShoutRequest.handler.responseText)
		{
			Shouts           = fetch_object('vbshout')
			Shouts.innerHTML = '<table cellpadding="1" cellspacing="3" border="0" width="95%" align="left">' + ShoutRequest.handler.responseText + '</table>'
			setTimeout('requestShouts()', 20000)
			<if condition="$vboptions[shout_messages_order]">
			document.getElementById('vbshout').scrollTop = 99999;
			</if>
		}
	}
}

function sb_CollectHV(sbForm)
{
	rString = ''
	inputObjs = sbForm.getElementsByTagName('input')
	for (i = 0; i < inputObjs.length; i++)
	{
		if (inputObjs[i].type == 'hidden' && inputObjs[i].value != '')
		{
			rString += '&' + inputObjs[i].name + '=' + PHP.urlencode(inputObjs[i].value)
		}
	}

	return rString
}

function postShout(formObj)
{
	doShout = new vB_AJAX_Handler(true)
	doShout.onreadystatechange(postedShout)

	if (postingShout)
	{
		alert('Posting in progress..')
		return false
	}

	Shout = formObj.shout.value

	if (Shout.replace(/ /g, '') == '')
	{
		alert('You must enter a shout!')
		return false
	}

	doShout.send('vbshout.php', 'do=shout&shout=' + PHP.urlencode(Shout) + sb_CollectHV(document.forms['vbshoutform']))
	sb_Clear()
	postingShout = true

	return false
}

function postedShout()
{
	if (doShout.handler.readyState == 4 && doShout.handler.status == 200)
	{
		postingShout = false
		requestShouts()
	}
}

function sb_Input_SC(sProperty, setting)
{
	eval('document.forms["vbshoutform"].shout.style.' + sProperty + ' = "' + setting + '"')
	eval('document.forms["vbshoutform"].' + sProperty + '.value = "' + setting + '"')
}

function getSelectionValue(eSelector)
{
	return eSelector.options[eSelector.options.selectedIndex].value == 'Default' ? '' : eSelector.options[eSelector.options.selectedIndex].value
}

function sb_PropChange(eSelector, sProperty)
{
	sb_Input_SC(sProperty, getSelectionValue(eSelector))
}

function sb_PropChange_Button_Value(sProperty)
{
	trueValue = ''
	switch (sProperty)
	{
		case 'fontWeight':
		falseValue = 'bold'
		break;

		case 'textDecoration':
		falseValue = 'underline'
		break;

		case 'fontStyle':
		falseValue = 'italic'
		break;
	}

	return (eval('document.forms["vbshoutform"].' + sProperty + '.value'))? trueValue : falseValue
}

function sb_PropChange_Button(cButton, sProperty)
{
	if (cButton.value.match(/\*/))
	{
		cButton.value = cButton.value.replace(/\s+\*/, '')
	}
	else
	{
		cButton.value = cButton.value + ' *'
	}

	sb_Input_SC(sProperty, sb_PropChange_Button_Value(sProperty))
}

function sb_Smilie(code)
{
	document.forms["vbshoutform"].shout.value += ' ' + code
	return false
}

function sb_Clear()
{
	document.forms["vbshoutform"].shout.value = ''
	return true;
}

function sb_Smilies(cButton)
{
	if (cButton.value.match(/\*/))
	{
		cButton.value = cButton.value.replace(/\s+\*/, '')
	}
	else
	{
		cButton.value = cButton.value + ' *'
	}
	
	document.getElementById('shout_emo').style.display = (document.getElementById('shout_emo').style.display == 'none')? '' : 'none'
}

requestShouts()
-->
</script>
So I can kind of understand whats happening here. When you submit a shout, it goes to postShout, creates a new AJAX handler, etc etc etc. What I do notice is that where a lot of other AJAX code involves passing entire html pages back and forth under the covers (stop me if I'm wrong), but here there seems to be just the single shout being passed through the AJAX.

Also, one of the major issues I'm having is that if you post a shout, then post another shout before the previous shout went through, the shoutbox deadlocks and won't refresh anymore. It has to do with the "isPosting" flag but I don't know any AJAX methods to force a call through... I guess it's getting hung up on the server end? Would it be worth it to implement a stack on the server end of things where shouts can be pushed until they can be popped out?

Here's the vbshout.php shout function, too.

Code:
// ---------------------------------------------------
// Grab Latest X Shouts
// ---------------------------------------------------

if ($_GET['do'] == 'latest')
{
	$NumShouts = $vbulletin->options['shout_display']; //For readability's sake
    $Output = array();
	  
	$Shouts = $DB->query('
			select s.*, u.username, u.usergroupid from '.TABLE_PREFIX.'shout s
			left join '.TABLE_PREFIX.'user u on (u.userid = s.s_by)
			order by s.sid desc limit ' . $NumShouts);

  	while ($Shout = $DB->fetch_array($Shouts))
	{
		$Shout['time']     = buildTime($Shout['s_time']);
		$Shout['s_shout']  = bbcodeparser($Shout['s_shout']);
		$Shout['style']    = '';
		$Shout['data']     = unserialize($Shout['s_data']);
		$Shout['username'] = fetch_musername($Shout, 'usergroupid');

		if ($Shout['data']['color'])
		{
			$Shout['style'] .= 'color:'.$Shout['data']['color'].';';
		}

		if ($Shout['data']['font'])
		{
			$Shout['style'] .= 'font-family:'.$Shout['data']['font'].';';
		}

		if ($Shout['data']['bold'])
		{
			$Shout['style'] .= 'font-weight:'.$Shout['data']['bold'].';';
		}

		if ($Shout['data']['underline'])
		{
			$Shout['style'] .= 'text-decoration:'.$Shout['data']['underline'].';';
		}

		if ($Shout['data']['italic'])
		{
			$Shout['style'] .= 'font-style:'.$Shout['data']['italic'].';';
		}

		if ($Shout['style'])
		{
			$Shout['s_shout'] = '<font style="'.$Shout['style'].'">'.$Shout['s_shout'].'</font>';
		}

		eval('$Output[] .= "' . fetch_template('forumhome_vbshout_shout') . '";');
	}

	if (isBanned($vbulletin->userinfo) && $vbulletin->options['shout_banned_perms'] > 0)
	{
		$Output = '';
		$Shout  = array(
				'time'     => buildTime(),
				'username' => 'System Reponse',
				's_shout'  => 'You are currently banned from the shoutbox',
			);

		eval('$Output .= "' . fetch_template('forumhome_vbshout_shout') . '";');
	}

	if (empty($Output))
	{
		$Output = '';
		$Shout  = array(
				'time'     => buildTime(),
				'username' => 'System Reponse',
				's_shout'    => 'No Current Shouts',
			);

		eval('$Output .= "' . fetch_template('forumhome_vbshout_shout') . '";');
	}
	else
	{
		if ($vbulletin->options['shout_messages_order'])
		{
			$Output = array_reverse($Output);
		}

		$Shouts = $Output;
		$Output = '';

		foreach ($Shouts as $Shout)
		{
			$Output .= $Shout;
		}
	}

	unset($Shouts, $Shout);

    echo $Output;

	exit;
}

// ---------------------------------------------------
// End Latest X Shouts
// ---------------------------------------------------

// ---------------------------------------------------
// Shout
// ---------------------------------------------------

if ($_POST['do'] == 'shout')
{
	$vbulletin->input->clean_array_gpc('p', array(
		'shout'	         => TYPE_NOHTML,
		'color'          => TYPE_NOHTML,
		'fontFamily'     => TYPE_NOHTML,
		'fontWeight'     => TYPE_NOHTML,
		'fontStyle'      => TYPE_NOHTML,
		'textDecoration' => TYPE_NOHTML,
	));

	$meShout = 0;

	if (!empty($vbulletin->GPC['shout']) && $vbulletin->userinfo['userid'] > 0 && !isBanned($vbulletin->userinfo))
	{

		$ShoutData = addslashes(serialize(array(
				'color'      => addslashes(convert_urlencoded_unicode($vbulletin->GPC['color'])),
				'font'       => addslashes(convert_urlencoded_unicode($vbulletin->GPC['fontFamily'])),
				'bold'       => addslashes(convert_urlencoded_unicode($vbulletin->GPC['fontWeight'])),
				'italic'     => addslashes(convert_urlencoded_unicode($vbulletin->GPC['fontStyle'])),
				'underline'  => addslashes(convert_urlencoded_unicode($vbulletin->GPC['textDecoration'])),
		)));

      
		$vbulletin->GPC['shout'] = convert_urlencoded_unicode($vbulletin->GPC['shout']);

		if (($vbulletin->GPC['shout'] = execCommand($vbulletin->GPC['shout'])) !== true)
		{
			$DB->query("
				insert into ".TABLE_PREFIX."shout
				(s_time, s_by, s_shout, s_data, s_me)
				values
				(".TIMENOW.", {$vbulletin->userinfo['userid']}, '".addslashes($vbulletin->GPC['shout'])."', '{$ShoutData}', $meShout)
			");
			
			$DB->query("UPDATE ".TABLE_PREFIX."user SET
                 shouts = shouts + 1 WHERE userid = {$vbulletin->userinfo['userid']}");
		}
	}
	exit;
}

// ---------------------------------------------------
// End Shout
// ---------------------------------------------------
It's a lot to read through if you're interested, but I don't have anything specific I'm looking for other than ways to tackle this problem. I don't need someone to write the AJAX for me, per se, but a good lead on some sources would be awesome and some ideas on the best, most efficient implementation would be awesome

l33t_1-1axx0r is offline   Reply With Quote