/*

 RPC-JS chat experiment
 ----------------------
 v1.0.20030925 | beta 1

 Works .. well, sometimes.
 And yeah, the code is a mess.

 scott_s03 at schillmania.com

 http://www.schillmania.com


 BACKGROUND
 ==========

 REALLY QUICK SUMMARY
 --------------------
 I was trying to come up with some way of getting multiple web clients to talk
 to each other without using plug-ins, etc.. ie. relying solely on client-side
 technology standard to 5.x+ browsers. This is what I've come up with so far.
 (I am working on this for fun, but with adding multiplayer functionality to my
 DHTML Scorched Earth project in mind.)


 QUICK SUMMARY
 -------------
 "Two-way" communication between multiple clients without page reloading (woo!)

 An iFrame is used to receive messages; HTTP requests are used for sending.

 If you use Mozilla, a PHP script acts as a proxy for "streaming" the RPC data,
 required in order to work around Mozilla cross-domain security stuff. A direct
 connection is established by the client when sending messages to the server.

 If you're using IE, a direct connection is also used for the RPC data stream;
 Cross-frame script communication is allowed in IE as long as the frame is in
 the same domain - It doesn't seem to mind a different port, unlike Mozilla.

 The PHP script connects to the broadcast server, a simple multithreaded app;
 the broadcast server listens on 2 ports, sending one-way data on each channel.
 The server is quite simple at this point, but features could easily be added.


 TECHNICAL SUMMARY
 -----------------
 An iFrame loads data from a socks broadcast server (modified HTTP/1.0, sorta.)

 This iFrame is the communications channel for receiving events (messages) from
 other clients and the server. I call it the "RPC Frame", as it forms the link.
 The iFrame is pointed to the broadcast server, where events are sent from.

 Javascript blocks are created "on-the-fly", and are sent by the server as it
 receives events from clients. The RPC frame is basically a regular old HTML
 page that never finishes loading because no "content-length" HTTP header is
 given, and the server closes the connection only if the client hangs up first.

 (Normally if a content-length header is given, eg. 1024 bytes, the client will
 automatically close the connection after 1024 bytes have been received. By not
 providing a length, the client will keep the connection until the server drops
 or the client hangs up. At least, this is how I believe it works!)

 Hence the "opening page" and "transferring data" messages you see in IE and
 Mozilla respectively, along with the animated "throbber" icons showing loading
 of data. There *is* data coming down the line, just very slowly ;)

 Because of cross-browser security issues, the RPC page must reside on the same
 server (and even port according to Mozilla apparently!) as the web page; for
 this reason, a proxy is used with Mozilla - under IE it'll connect directly.

 In either case, a "persistent" connection (ie. one that does not close) is
 opened between client and broadcast server. As the server receives events,
 they may be broadcasted to all connected clients depending on the event type.

 To post a message, the client loads a URL through a Javascript element and
 passes the message data as a parameter in the URL. This connection is made on
 a different port, and is made directly as there are no cross-browser security
 issues here (the script sent down at this point does not make any calls to the
 parent frame, but the capability is there and will likely be used later on.)


 QUIRKS/ISSUES
 -------------
 Mozilla Firebird complains about some XULElement stuff when receiving messages.

 Mozilla 1.3a is fine however.

 Netscape sometimes doesn't clue in at first, but all is well after a refresh.

 There seem to be some issues with the PHP-based proxy, a simpler CGI-based one
 may be a better idea. I suspect it has to do with a limitation on the number of
 instances of PHP allowed to run simultaneously on the web server, something to
 that effect - With multiple windows open on the same machine, the site seems to
 hang while loading. Without the PHP this is not an issue, however.

 Who knows, could be literally tons of other bugs here.

 - Scott | schillmania.com

*/


/*

  ADDITIONAL NOTES
  ----------------
  This has yet to be tested thoroughly and componentized (OO-style),
  allowing for customization and adaptibility to various applications.

*/


var clientList = null;
var connected = false;

function errorHandler(msg,file,line) {
  serverWarning('(Warning): error - line ' + line +': '+ msg,Math.random());
  return true;
}

function serverWarning(txt,rnd) {
  serverMessage(txt,'serverWarning',rnd);
}

function sendMessage(data) { // "post" to server (which broadcasts to connected clients) via JS URL
  var o = document.createElement('script');
  var url = 'http://schill.dyndns.org:7061';
  var m = [/'/g,/#/g,/</g,/>/g];
  var r = ['&lsquo;','_','&lt;','&gt;'];
  for (var i=0; i<m.length; i++) {
    data = data.replace(m[i],r[i]);
  }
  o.src = url + '?data=\''+unescape(userName)+unescape(data)+'\',null,'+parseInt(Math.random()*65535);
  document.getElementsByTagName('head')[0].appendChild(o);
}

function serverMessage(txt,className,rndData) { // method called from "streaming" RPC iFrame
  var o = document.createElement('p');
  o.className = 'rpcMessage'+(className?' '+className:'');
  o.style.className = 'rpcMessage'+(className?' '+className:'');
  var d = new Date();
  d.h = d.getHours();
  d.h -= d.h>12?12:0;
  if (d.h == 0) d.h = 12;
  d.m = d.getMinutes();
  d.min = d.m-(d.m%5);
  if (d.m<10) d.m = '0'+d.m;
  if (d.min<10) d.min = '0'+d.min;
  d.s = d.getSeconds();
  txt = '<img src="http://www.schillmania.com/interactive/webpad/clock/'+d.h+'_'+d.min+'.gif" alt="'+(d.h>12?d.h-12:d.h)+':'+d.m+':'+d.s+' '+(d.h>12?'AM':'PM')+'"/> ' + txt;
  if (txt.toLowerCase().indexOf('connection established')+1) {
    o.className = 'rpcMessage serverWarning'; // temporary workaround
    connected = true;
    setSubmit(connected);
  }
  o.innerHTML = unescape(txt);
  // document.getElementById('messageArea').appendChild(o);

  var oMsg = document.getElementById('rpcMessages'); // messageArea

  if (!oMsg.childNodes.length) {
    oMsg.appendChild(o);
  } else {
    oMsg.insertBefore(o,oMsg.childNodes[0]);    
  }
  // o.scrollIntoView();
  rpcReflow();
}

function setSubmit(state) {
  document.getElementById('mSubmit').disabled = state;
}

var intCount = 0;

function rpcConnect() {
  // window.onerror = errorHandler;
  var isIE = navigator.appVersion.indexOf('MSIE')+1;
  var useProxy = 1; // Force proxy use for time being
  document.getElementById('rpcMessages').style.display = 'block';
  clientList = new ClientList('rpcClients');
  var i = document.createElement('iframe');
  i.id = 'rpcFrame';
  i.style.visibility = 'hidden';
  i.onerror = errorHandler;
  if (isIE) i.onreadystatechange = function(){readyStateHandler(this,this.readyState);};
  i.src = (!useProxy?'http://'+document.domain+':7060':'/spring_03/rpc-js_proxy.php?r='+parseInt(Math.random()*1048576)); // IE can get away with direct access; Mozilla needs a proxy (cross-domain security restrictions apply to different PORTS on the same domain, apparently. Yikes.)
  // serverMessage('RPC URL: '+i.src,'serverMessage');
  serverMessage('Opening connection...','serverMessage');
  document.body.appendChild(i);
  function readyStateHandler(i,s) {
    // serverMessage('iFrame state: '+s,'serverMessage');
    if (s == 'complete') {
      serverWarning('The RPC server has likely disconnected and/or shut down. Try reconnecting.');
      connected = false;
    } else if (s == 'interactive') {
      intCount++;
      if (intCount > 6) {
        serverWarning('Unable to connect to RPC server - may be down. Try again later.');
        connected = false;
      }
    }
  }
}

function rpcDisconnect() {
  serverMessage('Disconnecting for use with detached window.','serverWarning');
  serverMessage('Closing connection...','serverMessage');
  document.getElementById('rpcFrame').src = 'about:blank';
}

function ClientList(o) {
  this.o = document.getElementById(o);
  this.clients = [];
  var d = null;
  this.Client = function() {
    this.name = '';
    // client object - name, time etc. properties for future use
  }
  this.addClient = function(index) {
    this.clients[index] = new this.Client();
  }
  this.removeClient = function(index) {
    // crap
    this.clients[index] = null;
    this.update();
  }
  this.clear = function() {
    this.clients = [];
  }
  this.update = function() {
    this.o.innerHTML = 'Users: &nbsp;';
    for (var i=0; i<this.clients.length; i++) {
      if (this.clients[i]) {
        d = document.createElement('img');
        d.src = 'image/user.gif';
        d.className = 'rpcClientIcon';
        this.o.appendChild(d);
      }
    }
  }
}

function setNameFromForm(oForm) {
  setName(oForm.getElementsByTagName('input')[0].value);
  oForm.getElementsByTagName('label')[0].innerHTML = 'Message';
  with (oForm.getElementsByTagName('button')[0]) {
    innerHTML = 'Send';
  }
  setSubmit(connected?false:true);
  // oForm.getElementsByTagName('input')[0].value = '';
  oForm.onsubmit = function() {
    return formSubmitHandler(this);
  }
  return false;
}

function formSubmitHandler(oForm) {
  sendMessage(oForm.getElementsByTagName('input')[0].value);
  oForm.getElementsByTagName('input')[0].value='';
  return false;
}


function setName(n) {
  userName = '<span>'+n+'</span>: ';
  document.getElementById('data').focus();
}

function rpcReflow() {
  if (!boxes) return false;
  if (boxes[5]) {
    boxes[5].reflow(); // hard-coded reference to related box - avoid funny corner stuff
  }
}

function errorHandler(msg,file,line) {
  serverWarning('(Warning): error - line ' + line +': '+ msg,Math.random());
  return true;
}

/*
// moved to main script reference.
window.onload = function() {
  document.getElementById('data').focus();
  connect();
}
*/