/****c* oos.comm.ajax/Pushlets
  *  NAME
  *    Pushlets - Class representing global pushlet client
  *  VERSION
  *    1.00
  *  AUTHOR
  *    Reinhard Holzmann
  *  CREATION DATE
  *    14/09/2006
  *  HISTORY
  *    1.00	Basic version
  *  SYNOPSIS
  *    -
  *  DESCRIPTION
  *    Class representing global pushlet client
  ***/
function Pushlets() {

	/***********************************************************/
	/**                         API                           **/   
	/***********************************************************/	
	
	var me=this;
	var objects = new Array();
	
	/***** PUBLC MEMBERS *****/
	me.Embedded = false;
	me.Joined = false;
	
	// the used default deserializer
	me.DefaultDeserializer = null;
	me.DefaultMode = "stream";
	
	/***** INNER FUNCTIONS *****/
	
	me.embed = function() {
		waitCounter = 0;
		p_embed();			
		me.Embedded = true;	
	}
	
	me.join = function(inMode) {
		joinCounter = 0;
		listenCounter = 0;
		if (typeof(inMode) == "undefined" || inMode == null)
			inMode = me.DefaultMode;
		p_join_listen(null, inMode);	
		Pushlets.Joined = true;	
	}	
	
	me.leave = function() {
		p_leave();	
		Pushlets.Joined = false;
	}		
	
	me.subscribe = function(inSubject) {
		if(typeof(inSubject) == "undefined" || inSubject == null)
			return false;		
		p_subscribe(inSubject, null);
		return true;
	}	
	
	me.unsubscribe = function(inSId) {
		if(typeof(inSId) == "undefined" || inSId == null)
			return false;
		p_unsubscribe(inSId);
		return true;
	}				
	
	/***** DIRECT USAGE METHOD *****/
	
	me.activate = function(inSubject, inCallback, inDeserializer, inMode) {
		
		// in this form of usage subject has to be unique
		var object;
		for(var i=0;i<objects.length; i++) {
			object = objects[i];
			if(object.getSubject() == inSubject) {
				return false;
			}
		}	
		
		var push = new PushObject(inSubject, inCallback, inDeserializer, inMode);
		return push.activate();
	}
	
	me.deactivate = function(inSubject) {
		// in this form of usage subject has to be unique
		var object;
		for(var i=0;i<objects.length; i++) {
			object = objects[i];
			if(object.getSubject() == inSubject) {
				return object.deactivate();
			}
		}		
		return false;
	}	
	
	me.change = function(inSubjectOld, inSubjectNew) {
		// in this form of usage subject has to be unique
		var object;
		for(var i=0;i<objects.length; i++) {
			object = objects[i];
			if(object.getSubject() == inSubjectOld) {
				return object.change(inSubjectNew);
			}
		}			
		return false;
	}	
	
	me.get = function(inSubject) {
		// in this form of usage subject has to be unique
		var object;
		for(var i=0;i<objects.length; i++) {
			object = objects[i];
			if(object.getSubject() == inSubject) {
				return object;
			}
		}		
		return null;
	}	
	
	me.contains = function(inSubject) {
		// test if the subject is listened to of any object
		return (!(me.get(inSubject)==null));
	}
	
	me.restore = function() {
		var  object;
		for(var i=0;i<objects.length; i++) {
			object = objects[i];
			if(!me.subscribe(object.getSubject())) {
				me.removeObject(Object);
				if(typeof(System) != "undefined")
					System.log("[Pushlets] Restoring subject " + object.getSubject() + " failed!");
			}
		}
	}
	
	/***** GETTER/SETTER *****/
	
	me.getSession = function() {
		return sessionId;
	}			
	

	/***** Objects vector functions*****/

	me.getObject = function(inIndex) {
		if(inIndex!=null && inIndex>=0 && inIndex<objects.length)
			return objects[inIndex];
	}

	me.addObject = function(inObject) {
		var lastIndex = objects.length;
		// add the given Element at the end of the Vector
		objects[lastIndex] = inObject;
		return lastIndex;		
	}
	
	me.removeObject = function(inObject) {
		var deleteIndex = me.indexOfObject(inObject);
		if(deleteIndex != -1)
			me.removeObjectAt(deleteIndex);		
	}
	
	
	me.removeObjectAt = function(inIndex) {
		if(inIndex!=null && inIndex>=0 && inIndex<objects.length) {
			var tempArray1 = objects.slice(0, inIndex);
			var tempArray2 = objects.slice(inIndex+1, objects.length);
			objects = tempArray1.concat(tempArray2);
		}			
	}	
	
	me.indexOfObject = function(inObject, inStartIndex) {
		var myIndex = inStartIndex ? inStartIndex : 0;
		
		if(myIndex>=0 && myIndex<objects.length) {
			for(; myIndex<objects.length; myIndex++) {
				if(objects[myIndex] == inObject) {
					return myIndex;
				}
			}
		}
		return -1;		
	}	
	
	me.sizeOfObjects = function() {
		return objects.length;
	}	
	
	/***********************************************************/
	/**                   EVENT FUNCTIONS                     **/   
	/***********************************************************/
	
	/*
		PUSHLET RESPONSE FUNCTIONS 
	*/		
	var onEvent = function(event) {
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][Event]"+event.toString());	
	}
	
	var onData = function(event) {
		var Object;
		var hsdata = event.get("hs_data");
		
		//correct double quotes
		hsdata=hsdata.replace(/_''/g, "\""); 
		hsdata=hsdata.replace(/x''/g, "''"); 	
			
		//correct escape sequences
		hsdata = hsdata.replace(/__n/g, "\n");
		hsdata = hsdata.replace(/x_n/g, "_n");		
		
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][onData]"+hsdata);
		
		for(var i=0;i<Pushlets.sizeOfObjects();i++) {
			var obj = Pushlets.getObject(i);
			if(event.get("p_subject")== obj.getSubject()) {
				// call callback
				obj.callback(hsdata);
			}	
		}		
	}
	
	var onJoinAck = function(event) {                                            
		if(typeof(OOS) != "undefined")                                          
			OOS.log("[Pushlet][JoinAck]"+event.toString());	                                                               
	                                                                                
		// is the session already set?                                          
		if(sessionId != null) {	                                                
			// callback: Pushlet frame is ready              
			me.onReady();                                                             
		}	                                                                
	}   
	
	 var onListenAck = function(event) {
		var Object;
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][ListenAck]"+event.toString());
			
		// callback: Pushlet listener ready
		me.onListen();	
	}
	
	var onLeaveAck = function(event) {
		var Object;
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][LeaveAck]"+event.toString());
			
		// callback
		me.onLeave();	
	}
	
	var onSubscribeAck = function(event) {
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][SubscribeAck]"+event.toString());	
		// give the sid to the first object in the array
		for(var i=0;i<Pushlets.sizeOfObjects();i++) {
			var obj = Pushlets.getObject(i);
			if(event.get("p_subject")== obj.getSubject() &&
				obj.getSId() == null) {
				// set the subject id of this pushlet object
				obj.setSId(event.get("p_sid"));
				
				// callback
				me.onSubscribe(event.get("p_subject"), event.get("p_sid"));
				return true;
			}	
		}	
	}
	
	var onUnsubscribeAck = function(event) {
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][UnsubscribeAck]"+event.toString());
			
		// callback
		me.onUnsubscribe(event.get("p_subject"));			
	}		
	
	var onHeartbeat = function(event) {
		//if(typeof(OOS) != "undefined")
		//	OOS.log("[Pushlet][Heartbeat]" + event.toString());
	}
	var onHeartbeatAck = function(event) {
		//if(typeof(OOS) != "undefined")
		//	OOS.log("[Pushlet][Heartbeat]" + event.toString());
	}	
	
	var onAbort = function(event) {
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][Abort]" + event.toString());
		
		// null sessionid
		Pushlets.Joined = false;
		throw ("[AjaxComm]Connection to server lost...");
	}
	
	var onNack = function(event) {
		if(typeof(OOS) != "undefined")
			OOS.log("[Pushlet][Nack]" + event.toString());
	}			
	
	
	
	
	/***********************************************************/
	/**                    PUSHLETS CORE                      **/   
	/***********************************************************/	
	
	/*
	 * Pushlet JS client library.
	 * NOTE: this should replace the js-pushlet-client.jsp file
	 * (since we can figure out pushletWebRoot from within JS)
	 *
	 * $Id: Pushlets.js,v 1.5 2006-11-07 23:39:30 reinhard Exp $
	 */
	var configJsURI = "js/oos/comm/pushlets/Pushlets.js"; 
	var configNetURI = 'js/oos/comm/pushlets/js-pushlet-net.html';
	var flag = false;
	var pushletWebRoot=null;
	var pushletURI;
	var pushletNetURI;
	var sessionId=null;
	var controlQueue = new Queue(100);
	var pushletNet;
	var listenMode=null;
	var listenSubject=null;
	
	var session_kick_secs = 8;
	var flipflop = false;
	var flipflop2 = false; 
	
	var restore = false; 
	
	var waitCounter = 0;
	var joinCounter = 0;
	var listenCounter = 0;
	
	var pconstructor = function()  {
		// Initialize various URLs
		_initURIs();
	}
	
	/************ Public application functions ******************/
	
	me.p_hbloop = function() {
	  // Loop forever as long not joined
	  if (sessionId && !flipflop) {
	  	
	  	// set flipflot to true - at server response hback it is set to false
	  	flipflop = true;
	  	
	  	// do heartbeat
	  	p_heartbeat();
	  	
		if(typeof(System) != "undefined")
			System.log("[Pushlets] Sending heartbeat...");
		
		setTimeout("Pushlets.p_hbloop()", session_kick_secs * 1000);
	  } else { 
	  	
	  	if(sessionId && !flipflop2) {
			flipflop2 = true;
	  		
	  		// do heartbeat
	  		p_heartbeat();
	  	
			if(typeof(System) != "undefined")
				System.log("[Pushlets] Sending 2nd heartbeat...");
		
			setTimeout("Pushlets.p_hbloop()", session_kick_secs * 1000);
		
	  	} else {
	  		
	  		Pushlets.Joined = false;
		  	sessionId = null;
	  		
	  		if(restore) {
	  			restore = false;  
		  		Pushlets.leave();
		  		Pushlets.join();
		  		Pushlets.restore();
		  		alert("Entered Restore");
		  		refreshPushlets();
		  		
	  		} else {
	  			alert("[Error] Lost communication channel. Press F5 to reload page!");	  
	  		}	
	  	}		
	  }
	}
	
	// Embed pushlet frame in page
	var p_embed = function(thePushletWebRoot) {
	  if (thePushletWebRoot) {
	    // Use this when webapp is not in same server e.g. with virtual hosts
	    pushletWebRoot = thePushletWebRoot;
		_initURIs();
	  }
	
	  // p_debug(flag, "p_embed", 'write ' +  pushletLayer);
	  // var pushletLayer = '<iframe id="pushletFrame" name="pushletFrame" src="' + pushletNetURI + '" style="visibility: hidden; width: 0px; height: 0px; border: 0px;"></iframe>';
	  // document.write(pushletLayer);
	  
	  var myIFrame = document.createElement("iframe");
	  myIFrame.id = "pushletFrame";
	  myIFrame.name = "pushletFrame";
	  myIFrame.src = pushletNetURI;
	  myIFrame.style.visibility = "hidden";
	  myIFrame.style.width = "0px";
	  myIFrame.style.height = "0px";
	  myIFrame.style.border = "0px";
	  
	  var myBody = document.getElementsByTagName('body').item(0);
	  myBody.appendChild(myIFrame);
	  
	  me._waitForPushletFrame();
	}
	
	// Join the pushlet server
	me.p_join = function() {
	   // Ignore (for now) if already joined
	   if (sessionId != null) {
	     return;
	   }

	   p_debug(flag, "p_join", 'joining..');
	
	  // Check if pushlet frame is loaded
	  if (pushletNet) {
	    // Pushlet iframe is ready for calls
	    pushletNet.setControlURI(pushletURI + '?p_event=join');
	  } else {
	
	    // Pushlet net iframe not loaded: continue waiting
	   	joinCounter = joinCounter + 1;
	  	if(joinCounter > 50)
	  		alert("[System Fault] Could not join to Push Engine. Press F5 to reload page!");
	  	else	
	    	setTimeout("Pushlets.p_join()", 200);
	  }
	}
	
	// Create data event channel with the server
	me.p_listen = function(subject, mode) {
	  // Optional initial subject to subscribe to
	  if (subject) {
		// Remember
	    listenSubject = subject;
	  }
	
	  // Optional mode (stream, pull, poll) i.s.o. default
	  if (mode) {
		// Remember
	    listenMode = mode;
	  }
	
	  // Loop forever as long not joined
	  if (sessionId) {
	    // ok we have joined
	
		// Create event URI for listen
	    var uri =  pushletURI + '?p_id=' + sessionId + '&p_event=listen';
	
	    // Optional subject to subscribe to
	    if (listenSubject) {
	      uri = uri + '&p_subject=' + listenSubject;
	    }
	
	    // Optional mode (stream, pull, poll) i.s.o. default
	    if (listenMode) {
	      uri = uri + '&p_mode=' + listenMode;
	    }
	    
	    // activate heartbeat loop
	    me.p_hbloop()
	
	    pushletNet.listen(uri);
	 	return;
	  }
	
	  // No join ack (sessionId) yet: wait until received
	  listenCounter = listenCounter + 1;
	  if(listenCounter > 50)
	  	alert("[System Fault] Could not listen to Push Engine. Press F5 to reload page!");
	  else	
	  	setTimeout("Pushlets.p_listen()", 200);
	
	}
	
	// Shorthand: Join the pushlet server and start listening immediately
	var p_join_listen = function(subject, mode) {
	  me.p_join();
	  me.p_listen(subject, mode);
	}
	
	// Leave the pushlet server
	var p_leave = function() {
	  // Ignore (for now) if already left
	  if (sessionId == null) {
	    return;
	  }
	
	  var uri = pushletURI + '?p_event=leave';
	  p_debug(flag, 'p_leave', 'leave uri=' + uri);
	  _sendControlURI(uri);
	}
	
	
	// Send heartbeat event; callback is onHeartbeatAck()
	var p_heartbeat = function() {
	  var uri = pushletURI + '?p_event=hb';
	  p_debug(flag, 'p_heartbeat');
	  _sendControlURI(uri);
	}
	
	// Publish to a subject
	var p_publish = function(subject, nvPairs) {
	  if (!subject) {
	    return false;
	  }
	
	  var uri = pushletURI + '?p_event=publish&p_subject=' + subject;
	
	  var args = p_publish.arguments;
	
	  // Put the arguments' name/value pairs in the URI
	    for (var i=1; i < args.length; i++) {
	     uri = uri + '&' + args[i] + '=' +  args[++i];
	    }
	
	  p_debug(false, 'p_publish', 'publish uri=' + uri);
	  _sendControlURI(uri);
	}
	
	// Subscribe to a subject with optional label
	var p_subscribe = function(subject, label) {
	  var uri = pushletURI + '?p_event=subscribe&p_subject=' + subject;
	  if (label) {
	    uri = uri + '&p_label=' + label;
	  }
	
	  p_debug(flag, 'p_subscribe', 'subscribe uri=' + uri);
	  _sendControlURI(uri);
	}
	
	 // Unsubscribe from a subject
	var p_unsubscribe = function(subscriptionId) {
	  var uri = pushletURI + '?p_event=unsubscribe';
	
	  if (subscriptionId) {
	     uri = uri + '&p_sid=' + subscriptionId;
	  }
	
	  p_debug(flag, 'p_unsubscribe', 'unsubscribe uri=' + uri);
	  _sendControlURI(uri);
	}
	
	
	// Get webroot for this webapp
	var p_getWebRoot = function() {
	  return pushletWebRoot;
	}
	
	// Get pushlet session id
	var p_getSessionId = function() {
	  return sessionId;
	}
	
	// Show debug window
	var p_setDebug = function(aFlag) {
	  flag = aFlag;
	  p_setNetDebug(aFlag);
	}
	
	// Show network debug window
	var p_setNetDebug = function(aFlag) {
	  pushletNet.p_setDebug(aFlag);
	}
	
	/************ Private functions ******************/
	
	/** CALLBACKS FROM pushletFrame ***/
	
	// Generic callback from server; this function is called from within the
	// Pushlet subscriber frame (see frames below).
	me._push = function(args) {
	  // Create a PushletEvent object from the arguments passed in
	  // push.arguments is event data coming from the Server
	  var event = new PushletEvent(args);
	 
	  p_debug(flag, '_push() from server: ', event.toString());
	
	  // Do action based on event type
	  var eventType = event.getEvent();
	
	  if (eventType == 'data') {
	    _doCallback(event, onData);
	  } else if (eventType == 'join-ack') {
	      sessionId = event.get('p_id');
	    _doCallback(event, onJoinAck);
	  } else if (eventType == 'listen-ack') {
	    _doCallback(event, onListenAck);
	
		// Send empty heartbeat event. This
		// silences many busy browser windows.
		// At least in Moz and IE.
		p_heartbeat();
	  } else if (eventType == 'hb') {
	   _doCallback(event, onHeartbeat);
	  } else if (eventType == 'hb-ack') {
	  	flipflop=false;
	  	flipflop2=false;
	    _doCallback(event, onHeartbeatAck);
	  } else if (eventType == 'leave-ack') {
	    sessionId = null;
	    _doCallback(event, onLeaveAck);
	  } else if (eventType == 'refresh-ack') {
	    _doCallback(event, onRefreshAck);
	  } else if (eventType == 'subscribe-ack') {
	    _doCallback(event, onSubscribeAck);
	  } else if (eventType == 'unsubscribe-ack') {
	    _doCallback(event, onUnsubscribeAck);
	  } else if (eventType == 'abort') {
	    _doCallback(event, onAbort);
	  } else if (eventType.match(/nack$/)) {
	     _doCallback(event, onNack);
	  }
	}
	
	var getWebRoot = function() {
		/** Return directory of this relative to document URL. */
		if (pushletWebRoot != null) {
			return pushletWebRoot;
		}
		
		//derive the baseDir value by looking for the script tag that loaded this file
		var head = document.getElementsByTagName('head')[0];
		var nodes = head.childNodes;
		for (var i = 0; i < nodes.length; ++i) {
			var src = nodes.item(i).src;
			if (src) {
				var index = src.indexOf(configJsURI);
				if (index >= 0) {
					pushletWebRoot = src.substring(0, index);
					//alert("found:"+pushletWebRoot);
					break;
				}
			}
		}
		return pushletWebRoot;
	}
	
	var _initURIs = function() {
	  pushletURI = getWebRoot() + 'pushlet.srv';
	  pushletNetURI = getWebRoot() + configNetURI;
	}
	
	var _onUnload = function() {
	  p_debug(true, "pushlet-lib", "_onUnload() called");
	}
	
	var _onBeforeUnload = function() {
	  p_debug(true, "pushlet-lib", "_onBeforeUnload() called");
	}
	
	var _onStop = function() {
	  p_debug(true, "pushlet-lib", "_onStop() called");
	}
	
	var _doCallback = function(event, cbFunction) {
	  // Do specific callback function if provided by client
	  if (cbFunction) {
	    // Do specific callback like onData(), onJoinAck() etc.
	    cbFunction(event);
	  } else if (Pushlets.onEvent) {
	    // general callback onEvent() provided to catch all events
	    Pushlets.onEvent(event);
	  }
	}
	
	/** CALLS TO pushletFrame ***/
	
	
	var _sendControlURI = function(uri) {
	  if (controlQueue.isFull()) {
	    // TODO divert to errpage
	    alert('serious problem: control queue is full');
	    // no sense going on
	    return;
	  }
	
	  if (sessionId == null) {
	     controlQueue.enqueue(uri);
	     me._processControlQueue();
	    return;
	  }
	
	    // All clear to send immediately ?
	  if (controlQueue.isEmpty()) {
	    if (pushletNet.isControlReady()) {
	      // Ok send direct
	      uri = uri + '&p_id=' + sessionId;
	      pushletNet.setControlURI(uri);
	    } else {
	      controlQueue.enqueue(uri);
	    }
	  } else {
	      // Queue not empty
	      controlQueue.enqueue(uri);
	  }
	
	  if (!controlQueue.isEmpty()) {
	     me._processControlQueue();
	  }
	}
	
	me._processControlQueue = function() {
	  if (controlQueue.isEmpty()) {
	     // all done
	     return;
	  }
	
	  if (sessionId != null) {
	    // Dequeue next control URI if pushletFrame ready
	    if (pushletNet.isControlReady()) {
	      var uri = controlQueue.dequeue() + '&p_id=' + sessionId;
	      pushletNet.setControlURI(uri);
	    }
	  }
	
	  // Loop forever as long queue is not empty
	   if (!controlQueue.isEmpty()) {
	     setTimeout("Pushlets._processControlQueue()", 50);
	   }
	 }
	
	
	me._waitForPushletFrame = function() {
	  // Loop forever as long net uri not ready
	  if (self.pushletFrame && self.pushletFrame.isLoaded && self.pushletFrame.isLoaded()) {
	    pushletNet = self.pushletFrame;
	 	return;
	  }
	  
	  waitCounter = waitCounter + 1;
	  if(waitCounter > 20)
	  	alert("[System Fault] Push Engine could not be started. Press F5 to reload!");
	  else	
	  	setTimeout("Pushlets._waitForPushletFrame()", 200);
	}
	

	
	/* Class to represent nl.justobjects.pushlet.Event in JavaScript.
	   Arguments are an array where args[i] is name and args[i+1] is value
	*/
	var PushletEvent = function(args) {
	   // Member variable setup; the Map stores the N/V pairs
	   this.map = new Map();
	
	   // Member function setup
	   this.getSubject = PushletEventGetSubject
	   this.getEvent = PushletEventGetEvent
	   this.put = PushletEventPut
	   this.get = PushletEventGet
	   this.toString = PushletEventToString
	   this.toTable = PushletEventToTable
	
	   // Put the arguments' name/value pairs in the Map
	   for (var i=0; i < args.length; i++) {
	     this.put(args[i], args[++i] );
	   }
	}
	
	// Get the subject attribute
	var PushletEventGetSubject = function() {
	  return this.map.get('p_subject')
	}
	
	// Get the subject attribute
	var PushletEventGetEvent = function() {
	  return this.map.get('p_event')
	}
	
	// Get event attribute
	var PushletEventGet = function(name) {
	  return this.map.get(name)
	}
	
	// Put event attribute
	var PushletEventPut = function(name, value) {
	  return this.map.put(name, value)
	}
	
	var PushletEventToString = function() {
	  return this.map.toString();
	}
	
	// Convert content to HTML TABLE
	var PushletEventToTable = function() {
	  return this.map.toTable();
	}
	
	/*************** Debug utility *******************************/
	var timestamp = 0
	var debugWindow = null
	var messages = new Array()
	var messagesIndex=0
	
	/** Send debug messages to a (D)HTML window. */
	var p_debug = function(flag, label, value) {
	
	   // Only print if the flag is set
	   if (!flag) {
	      return;
	   }
	
	      var funcName="none";
	
	      // Fetch JS function name if any
	      if (p_debug.caller) {
	        funcName = p_debug.caller.toString()
	        funcName = funcName.substring(9, funcName.indexOf(")") + 1)
	      }
	
	      // Create message
	      var msg = "-" + funcName + ": " + label + "=" + value
	
	      // Add optional timestamp
	      var now = new Date()
	      var elapsed = now - timestamp
	      if (elapsed < 10000) {
	         msg += " (" + elapsed + " msec)"
	      }
	
	      timestamp = now
	
	    // Show.
	
	  if ((debugWindow == null) || debugWindow.closed) {
	    debugWindow = window.open("","p_debugWin","toolbar=no,scrollbars=yes,resizable=yes,width=600,height=400");
	  }
	
	  // Add message to current list
	  messages[messagesIndex++] = msg
	
	  // Write doc header
	  debugWindow.document.writeln('<html><head><title>Pushlet Debug Window</title></head><body bgcolor=#DDDDDD>');
	
	    // Write the messages
	    for (var i=0; i < messagesIndex; i++) {
	    debugWindow.document.writeln('<pre>' + i + ': ' + messages[i] + '</pre>');
	  }
	
	  // Write doc footer and close
	  debugWindow.document.writeln('</body></html>');
	  debugWindow.document.close();
	  debugWindow.focus();
	}  
	
	
	/***********************************************************/
	/**                  PUSHLETS UTILITIES                   **/   
	/***********************************************************/

	/************** Util classes *******************************/
	
	/** NV pair object */
	function NameValuePair(name, value) {
	   this.name = name;
	   this.value = value;
	}
	
	/** Simple Map object to store array of name/value pairs */
	function Map() {
	  // Data members
	   this.index = 0;
	   this.map = new Array();
	
	   // Function members
	   this.get = MapGet;
	   this.put = MapPut;
	   this.toString = MapToString;
	   this.toTable = MapToTable;
	}
	
	/** get() */
	function MapGet(name) {
	   for (var i=0; i < this.index; i++) {
	     if (this.map[i].name == name) {
	       return this.map[i].value;
	     }
	   }
	   return '';
	}
	
	/** put() */
	function MapPut(name, value) {
	  this.map[this.index++] = new NameValuePair(name, value);
	}
	
	/** To HTML string */
	function MapToString() {
	    var res = '';
	
	   for (var i=0; i < this.index; i++) {
	     res = res + this.map[i].name+'='+this.map[i].value+'\n';
	   }
	   return res;
	}
	
	/** To HTML table */
	function MapToTable() {
	    var res = '<table border=1 cellpadding=3>';
	   var styleDiv = '<div style="color:black; font-family:monospace; font-size:10pt; white-space:pre;">'
	
	   for (var i=0; i < this.index; i++) {
	     res = res + '<tr><td bgColor=white>'+styleDiv+this.map[i].name+'</div></td><td bgColor=white>'+styleDiv+this.map[i].value+'</div></td></tr>';
	   }
	   res += '</table>'
	   return res;
	}
	
	/** Simple FIFO Queue class */
	function Queue(aCapacity) {
	  // Data members
	   this.capacity = aCapacity;
	   this.size = 0;
	   this.arr = new Array();
	   this.front = 0;
	   this.rear = 0;
	
	   // Function members
	   /** enqueue() */
	   this.enqueue = function(item) {
		    if (this.isFull()) {
		      alert('queue full !!');
		      return;
		    }
		
		   	  this.arr[this.rear] = item;
			  this.rear = this.next(this.rear);
			  this.size++;
			}
		
	   /** dequeue() */	
	   this.dequeue = function QueueDequeue(name) {
		   if (this.isEmpty()) {
		      alert('queue empty !!');
		      return;
		    }
		  var temp = this.arr[this.front];
		  this.arr[this.front] = null;
		  this.front = this.next(this.front);
		  this.size--;
		  return temp;
		}
		
	   /** Circular counter. */	
	   this.next = function(index) {
		  return (index + 1 < this.capacity ? index + 1 : 0);
		}
		
	   this.isFull = function() {
		  return this.size == this.capacity;
		}
	   
	   this.isEmpty = function() {
		  return this.size == 0;
		}	
	}
	
	pconstructor();
}
	
/***********************************************************/
/**                      SERIALIZER                       **/   
/***********************************************************/

Pushlets.prototype.JsonDefaultDeserializer = function(inJson) {

	//correct escape sequences \n\r
//	inJson = inJson.replace(/\n/g, "\\n");
//	inJson = inJson.replace(/\r/g, "\\r");

	// extract json Param to object
	var jsonObject = JSON.parse(inJson);
	return jsonObject;
}

Pushlets.prototype.XMLDefaultDeserializer = function(inXML) {
	// todo
	alert("PushObject default XML Deserializer not implemented yet!");
	return null;
}		

/***********************************************************/
/**           USER CALLBACKS - DO NOT EDIT                **/   
/***********************************************************/	
Pushlets.prototype.onReady = function(){};
Pushlets.prototype.onListen = function() {};
Pushlets.prototype.onLeave = function() {};
Pushlets.prototype.onSubscribe = function(inSubject, inSid) {};
Pushlets.prototype.onUnsubscribe = function(inSubject) {};


