Web 2.0 chat. I wrote this from scratch, after looking at PHPFreeChat's poor design...
Uses PHP5, Javascript, MySQL, and a virtual IFrame Object. no activex, and works on mac, opera, Moz., and IE5+.
from
http://www.galacticutopia.com4 files
1.)
This brings up the 2 frames. You want 2 because you dont want your menu frame to refresh --only the chat frame.
<html>
<head>
<title>Galactic Utopia Chat</title>
</head>
<frameset rows="70%,30%" >
<frame src="chatframe.php" name="chatframe" />
<frame src="chatmenu.php" name="chatmenu" />
</frameset>
</html>
2.)
Frame1:the chat
<?
header ("Content-type: text/html; charset=utf-8");
include ('your_mysql_dbconnect.inc'); //init your user session with mysql here
$chattext=playerinfo($gplayerid,"chat");
// i run my sql queries through a set/get function. so there is also
setplayerinfo(id,"chat",data...)
$gname=galaxyinfo("galaxyname"); //you can have infinite chat rooms, private or global this way and name them
?>
<html>
<body>
<body onload="scroll(0,9999)" >
<h3>
<div class="gradient">
<?
print <<<END
$gname COMM Channel<br /><i>
</div>
</h3>
END;
echo $chattext
?>
</body>
</html>
3.)
Frame2:the chat menu. allows you to select channel, exit, or clear chat windows
<?
include ('your_mysql_dbconnect.inc'); //init your user session with mysql here
//server scalability. unlike most ajax chats, this thing throttles itself down to reduce bandwidth if user leaves session open and pc on longer than approx 10 mins.
define("CHATREFRESHRATE", 8 );
define("CHATTHROTTLEDOWN",40);
$players=0;
$sql = "SELECT * FROM PLAYERS WHERE GALAXYID=$ggalaxyid ORDER BY ID"; //must collect all players to build the chat room list
$rs=mysql_query($sql); if (mysql_error()) { print $sql."pcollect: Database ERROR: ". mysql_error(); }
while ($row = mysql_fetch_array($rs))
{
$playername[$players]=$row['empirename'];
if ($row['pop']==0) $playername[$players]="---";
$players++;
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Galactic Utopia Chat</title>
<script type="text/javascript" >
var IFrameObj; // harntroX iNsAne IfrRamE object.
function callToServer(URL) {
if (!document.createElement) {return true};
var IFrameDoc;
if (!IFrameObj && document.createElement) {
// modern browser attempt
try {
var tempIFrame=document.createElement('iframe');
tempIFrame.setAttribute('id','RSIFrame');
tempIFrame.style.border='0px';
tempIFrame.style.width='0px';
tempIFrame.style.height='0px';
IFrameObj = document.body.appendChild(tempIFrame);
if (document.frames) { IFrameObj = document.frames['RSIFrame']; }
} catch(exception) { alert ("Get a Real Browser !");return false; }
} //document.createElement attempt
if (navigator.userAgent.indexOf('Gecko') !=-1 && !IFrameObj.contentDocument) {
// we have to give NS6 a fraction of a second
setTimeout('callToServer('+URL+')',10);
return false;
}
if (IFrameObj.contentDocument) {
// For NS6
IFrameDoc = IFrameObj.contentDocument;
} else if (IFrameObj.contentWindow) {
// For IE5.5 and IE6
IFrameDoc = IFrameObj.contentWindow.document;
} else if (IFrameObj.document) {
// For IE5
IFrameDoc = IFrameObj.document;
} else {
return true;
}
IFrameDoc.location.replace(URL);
return false;
}
function clicksend()
{
if (document.formchat.thetext.value!="")
{ callToServer("submitchat.php?id="+document.formchat.selectchannel.value+"&str="+document.formchat.thetext.value);
document.formchat.thetext.value = "";
return ;
}
else //keepalive
{
if (jupdatechat>999) {edits=1;refresh();}
}
}
function clickclear()
{
callToServer("submitchat.php?id="+document.formchat.selectchannel.value+"&str=~");
document.formchat.thetext.value = "";
clearTimeout(timer);
timer=setTimeout("refresh()", jupdatechat*1000 );
}
function clickexit()
{
parent.close();
}
<?
$throttlechatrate=CHATTHROTTLEDOWN;
$refreshchatrate=CHATREFRESHRATE;
print <<<END
jupdatechat=$refreshchatrate;
jthrottlechat=$throttlechatrate;
END;
?>
function refresh()
{
parent.chatframe.location.replace("chatframe.php"); //called as embedded iframe
clearTimeout(timer);
if (edits==0) jupdatechat++; //throttle down if unused
<?
print <<<END
if ((edits==1) && (jupdatechat>999)) jupdatechat=$refreshchatrate; //throttle up if activity detected
if ((edits==1) && (jupdatechat<99)) jupdatechat=$refreshchatrate;
END;
?>
if (jupdatechat>jthrottlechat) { parent.chatframe.location.replace("img/gkonlogo-sm.jpg");jupdatechat=9999;}
timer=setTimeout("refresh()", jupdatechat*1000 );
edits=0;
return false;
}
function jsmaxlength(elementId)
{
edits=1;
if (elementId)
{
strlength= ( elementId.getAttribute ? parseInt(elementId.getAttribute("maxlength")) : "" );
if ((elementId.getAttribute) && (elementId.value.length>strlength)) elementId.value=elementId.value.substring(0,strlength);
}
else return false;
}
var timer;
var edits=0;
function initdlg()
{
timer=setTimeout("refresh()", jupdatechat*1000 );
if(top.opener==null ) document.formchat.exit.value="";
}
</script>
</head>
<body class="gkonstyle" style="text-align: left" onload="initdlg()" oncontextmenu="return false;">
<form name="formchat" action="" method="post" >
<center>
<textarea name="thetext" rows="3" cols="58" maxlength="200" onkeyup="return jsmaxlength(this)" >
</textarea>
<select style="width: 165; font-size: 16;" name="selectchannel" >
<option value=0 selected >Global Broadcast</option>
<?
for($i=0;$i<$players;$i++)
{
$_j=$i+1;
$_str=$playername[$i];
if ($i+1==$gplayerid) {$_str="Note to Self...";}
print <<<END
<option value="$_j">$_str</option>
END;
}
?>
</select>
<input name="exit" type="button" value="Exit" onclick="clickexit()" />
<input name="clr" type="button" value="Clear" onclick="clickclear()" />
<input name="send" type="button" value="Send" onclick="clicksend()"/>
</form>
</body>
</html>
4.)
Finally, the PHP that is called when the Iframe Object is signalled by JavaScript clickSend()
-----------------------------------
<?
$playerid=$_GET['id'];
$str=$_GET['str'];
$mode=1;
if ($str=="~") $mode=0;
else
{
// if (substr_count ($str,"<BR>") >

$str="?";
$str=str_replace("'", ' ', $str); // single quotes gone
$str=str_replace("<BR>", ' ', $str); // linefeeds gone
$str=str_replace("<", ' ', $str); // < gone
$str=str_replace(">", ' ', $str); // > gone
$str=preg_replace('/\s\s+/', ' ', $str); // whitespace gone
makevalid($str,"chat"); // force validity with pregmatch
}
setplayerinfo($gplayerid,"chatstate",0);
if ($mode)
{ $srcname=playerinfo($gplayerid,"empirename");
$destname=playerinfo($playerid,"empirename");
}
// PASS1 - write to your own chat
// ------------------------------------------------------------------------------
if ($mode==0) //mode 0=start new chat log
{
$data="";
setplayerinfo($gplayerid,"chatlines",0);
}
else //appending to chat on DISK, SLOW
{
if (!$playerid) $data=playerinfo($gplayerid,"chat")."<br><font class=gkonturq >".$srcname." » ".$str."</font>";
else $data=playerinfo($gplayerid,"chat")."<br><font class=gkonturq >".$srcname." &#64; ".$destname." » ".$str."</font>";
$chatlines= playerinfo($gplayerid,"chatlines") + 1;
setplayerinfo($gplayerid,"chatlines",$chatlines);
}
setplayerinfo($gplayerid,"chat",$data); //direct write every time, sucks : TODO blob append
// ------------------------------------------------------------------------------
// PASS2A - write to single player's chat. Always write, even if offline
if (($playerid>0)&&($gplayerid!=$playerid)&&($mode)) // filter
{
$data=playerinfo($playerid,"chat")."<br>".$srcname." &#64; ".$destname." » ".$str;
$chatlines= playerinfo($playerid,"chatlines") + 1;
$sql="UPDATE PLAYERS SET
CHATLINES=$chatlines,
CHATSTATE=$gplayerid,
CHAT='$data' WHERE GALAXYID=$ggalaxyid AND ID=$playerid";
$rs=mysql_query($sql);
if (mysql_error()) { print $sql."setchatone: Database ERROR: " . mysql_error(); }
}
// ------------------------------------------------------------------------------
// or ... PASS2B - write to GLOBAL chat. ONLY Write if online, check user session
if (($playerid==0)&&($mode))//no filter broadcast to all
{
//collect
$players=0;
$sql = "SELECT * FROM PLAYERS WHERE GALAXYID=$ggalaxyid AND ID<>$gplayerid AND POP>0 ORDER BY ID";
$rs=mysql_query($sql); if (mysql_error()) { print $sql."setchatall1: Database ERROR: " . mysql_error(); }
while ($row = mysql_fetch_array($rs))
{
$userid=$row["userID"];
$online=sessioninfo($userid,"userID");
if (!$online) continue;
$plrid[$players]=$row["ID"];
$chatlinesa[$players]=$row["chatlines"] + 1;
$cdata[$players]=$row['chat']."<br>".$srcname." » ".$str;
$players++;
}
for($i=0;$i<$players;$i++)
{
$sql="UPDATE PLAYERS SET
CHATLINES=$chatlinesa[$i],
CHAT='$cdata[$i]' WHERE GALAXYID=$ggalaxyid AND ID=$plrid[$i]";
$rs=mysql_query($sql);
if (mysql_error()) { print $sql."setchatall2: Database ERROR: " . mysql_error(); }
}
}
?>
//JAVASCRIPT RPC CALLBACK SERVER EVENT HANDLER TRIGGER. This calls Refresh() in the chatmenu.php, file 3. above, which in turn reloads file 2., which is the chatframe itself.
<html>
<HEAD>
<script type="text/javascript">
window.parent.refresh();
</script>
</HEAD>
</html>
WooHooo !!!!
All kinds of security is in here ..limiting data size to 200 bytes, logging how many chat lines the user inputs to determine if its a spam bot, preventing sql/html injection and linefeeds.