	if (typeof wps_loggedInFuncs == "undefined")
		wps_loggedInFuncs = new Object();
	if (typeof wps_loggedOutFuncs == "undefined")
		wps_loggedOutFuncs = new Object();
	if (typeof wps_userStatusFuncs == "undefined")
		wps_userStatusFuncs = new Object();
	if (typeof wps_userEnteredFuncs == "undefined")
		wps_userEnteredFuncs = new Object();
	if (typeof wps_userLeftFuncs == "undefined")
		wps_userLeftFuncs = new Object();

	var defaultDynamicPersonTagURL = "/wps/myportal/dynamicpersontag";	
	var registeredInstantMessagingEvents = false;
	var dynamicPersonTagJSArray = new Array();
	var dynamicPersonTagJSArray_counter = 0;
	var personjsSTLinksWatchNames = "";
	var persontag_isLoggedIn = false;

	function releaseDynamicPersonTag() {
		for (var x=0; x < dynamicPersonTagJSArray.length; x++) {
			evaluateJavaScriptBlock(dynamicPersonTagJSArray[x]);
		}
		dynamicPersonTagJSArray_counter = 0;
		dynamicPersonTagJSArray = new Array();
	}

	function evaluateJavaScriptBlock(js) {
		var myscript;
		// IE
		if (typeof ActiveXObject != "undefined") {
			myscript = document.createElement("<script>");
			myscript.text = js;
			document.body.appendChild(myscript);
		}
		// Mozilla
		else {
			myscript = document.createElement("script");
			myscript.text = js;
			document.getElementsByTagName('head')[0].appendChild(myscript);
		}
	}

	var MS_XMLHTTP_TYPES = new Array(
		"MSXML2.XMLHTTP.4.0",
		"MSXML2.XMLHTTP.3.0",
		"MSXML2.XMLHTTP",
		"Microsoft.XMLHTTP"
		);
	
	// Returns the raw string from the server.  Caller has to extract the desired data from it.
	function getDataFromServer(url) {
		var maindata = "";
		var myurl = document.location.protocol + "//" + document.location.host + url;
		var xmlhttp = null;
		var success = false;
		var ex = null;
		
		// Mozilla
		if (window.XMLHttpRequest != null) {
			xmlhttp = new XMLHttpRequest();
		}
		// IE
		else if (window.ActiveXObject != null)
		{
			for (var i = 0; i < MS_XMLHTTP_TYPES.length && !success; i++) {
				try {
					xmlhttp = new ActiveXObject(MS_XMLHTTP_TYPES[i]);
					success = true;
				}
				catch (ex) {}
			}
			if (!success)
				throw "Error in dynamic person tag.  Unable to create an XMLHTTP object.";
		}
				
		var spliturl = myurl.split("?");

		xmlhttp.open("POST", spliturl[0], false);
		xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		xmlhttp.send(spliturl[1]);
		// Check the HTTP response status code for "OK".
		if (xmlhttp.status != 200) {
			throw "HTTP request for \"" + spliturl[0] + "\" failed with status \"" +
				xmlhttp.status + " " + xmlhttp.statusText + "\"";
		}
		else {
			maindata = xmlhttp.responseText;
		}
		return maindata;
	}	
	
	//	
	// javascript:invokeDynamicPersonTag("CN=wpsadmin,O=eCAT", "LDAPDN", "wpsadmin")
	//
	function invokeDynamicPersonTag (userId, userIdType, displayName, contextArray, skin, url) {
	
		// ********************************************************
		// NOTE THIS MAKES IT SO NO DISPLAY NAMES ARE PASSED IN
		// THIS IS DONE BECAUSE OF SPR #NKUO5ZQBU8 WHEREBY PASSING
		// IN DBCS DISPLAY NAMES CAUSES NAME CORRUPTION
		// ********************************************************
		displayName = "";
	
		var theHTML = "";
		var index;
		
		// Default path to the hidden portlet
		if (typeof url == "undefined" || url == null || url == "") {
			url = defaultDynamicPersonTagURL;
		}
				
		// Must have a userId		
		if (typeof userId == "undefined" || userId == null || userId == "") {
			return;
		}
		
		// Guess the type if not given
		if (typeof userIdType == "undefined" || userIdType == null || userIdType == "") {
			index = userId.indexOf("@");
			if (index >= 0)
				userIdType = "EMAIL";
			else
				userIdType = "LDAPDN";
		}

		// Create the complete URL to the hidden portlet
		
		var params = "value=" + URLEncoder(userId) + "&valueType=" + userIdType;
		
		// Add the displayName to the URL
		if (typeof displayName != "undefined" && displayName != null && displayName != "") {
			params += "&displayName=" + URLEncoder(displayName);
		}
		
		// Add the skin to the URL
		if (typeof skin != "undefined" && skin != null && skin != "") {
			params += "&skin=" + skin;
		}
		
		// Add the other params to the URL
		if (typeof contextArray != "undefined" && contextArray != null && contextArray != "") {
			for (var item in contextArray) {
				if (contextArray[item] != "undefined" && contextArray[item] != null && contextArray[item] != "") {
					params += "&" + item + "=" + contextArray[item];
				}
			}
		}
		
		//strip off the last bit, since it interferes with the backend getting of params.  ie. #7_0_DO
		index = url.lastIndexOf("#");
		if (index >= 0) {
			url = url.substr(0, index);
		}

		// This means that they passed in a url created by the urlGeneration tag
		if (url.indexOf("markerstart") >= 0) {	
			url = url.replace(/markerstart=markerend/g, params);
		}
		else {
			url += "?" + params;
		}

		var maindata = getDataFromServer(url);
					
		// Find the section in the string where the Person Tag output lives
		index = maindata.indexOf("<dynamicpersontagdata>");
		var start = index + "<dynamicpersontagdata>".length;
		maindata = maindata.substr(start, maindata.indexOf("</dynamicpersontagdata>")-start);

		// Get the HTML
		index = maindata.indexOf("<script");
		theHTML = maindata.substr(0, index);	

		// Get the JavaScript and put it into the DOM
		index = maindata.indexOf("<script");
		index = maindata.indexOf(">", index);
		var js = maindata.substr(index+1, maindata.indexOf("<\/script>")-(index+1));

		dynamicPersonTagJSArray[dynamicPersonTagJSArray_counter++] = js;

		return(theHTML);
	}

	// NEEDSWORK - better error-checking...assuming 3 identical length arrays for now
	// NEEDSWORK - put XML data island code into a common function
	function invokeArrayOfDynamicPersonTags(userIds, userIdTypes, displayNames, contextArray, skin, url) {
	
		// ********************************************************
		// NOTE THIS MAKES IT SO NO DISPLAY NAMES ARE PASSED IN
		// THIS IS DONE BECAUSE OF SPR #NKUO5ZQBU8 WHEREBY PASSING
		// IN DBCS DISPLAY NAMES CAUSES NAME CORRUPTION
		// ********************************************************
		displayNames = new Array();
	
		var writeOutPersonCallArray = new Array();
		var index;

		// Default path to the hidden portlet
		if (typeof url == "undefined" || url == null || url == "") {
			url = defaultDynamicPersonTagURL;
		}
				
		// Must have a userIds Array
		if (typeof userIds != "object" || userIds == null || userIds == "") {
			return;
		}

		// Create the complete URL to the hidden portlet
		
		var params = "";
		for (var x=0; x < userIds.length; x++) {
			params += (x > 0 ? "&" : "") + "value" + x + "=" + URLEncoder(userIds[x]) + "&valueType" + x + "=" + userIdTypes[x];
		
			// Add the displayName to the URL
			if (typeof displayNames[x] != "undefined" && displayNames[x] != null && displayNames[x] != "") {
				params += "&displayName" + x + "=" + URLEncoder(displayNames[x]);
			}
		}
		
		// Add the skin to the URL
		if (typeof skin != "undefined" && skin != null && skin != "") {
			params += "&skin=" + skin;
		}
		
		// Add the other params to the URL
		if (typeof contextArray != "undefined" && contextArray != null && contextArray != "") {
			for (var item in contextArray) {
				if (contextArray[item] != "undefined" && contextArray[item] != null && contextArray[item] != "") {
					params += "&" + item + "=" + contextArray[item];
				}
			}
		}
		
		//strip off the last bit, since it interferes with the backend getting of params.  ie. #7_0_DO
		index = url.lastIndexOf("#");
		if (index >= 0) {
			url = url.substr(0, index);
		}
		
		// This means that they passed in a url created by the urlGeneration tag
		if (url.indexOf("markerstart") >= 0) {	
			url = url.replace(/markerstart=markerend/g, params);
		}
		else {
			url += "?" + params;
		}

		var maindata = getDataFromServer(url);
		var onePersonData = null;
					
		for (var x=0; x < userIds.length; x++) {
			// Find the section in the string where the Person Tag output lives
			index = maindata.indexOf("<dynamicpersontagdata" + x + ">");
			var start = index + ("<dynamicpersontagdata" + x + ">").length;
			onePersonData = maindata.substr(start, maindata.indexOf("</dynamicpersontagdata" + x + ">")-start);
			
			// Get the HTML
			index = onePersonData.indexOf("<script");
			theHTML = onePersonData.substr(0, index);	

			// Get the JavaScript and put it into the DOM
			index = onePersonData.indexOf("<script");
			index = onePersonData.indexOf(">", index);
			var js = onePersonData.substr(index+1, onePersonData.indexOf("<\/script>")-(index+1));
			dynamicPersonTagJSArray[dynamicPersonTagJSArray_counter++] = js;
		
			writeOutPersonCallArray[x] = theHTML;
		}
		return(writeOutPersonCallArray);
	}		
		
		
		function addPeopleMenuMoreMenuItems(menu) {			
			if (typeof peoplemenu_more != "undefined") {
			    var items = peoplemenu_more.items;
			    var actions = peoplemenu_more.actions;
			    for (i = 0; i < items.length; i++) {
			        if (actions[i].toLowerCase().indexOf("javascript:") == -1) {
			             menu.add( new UilMenuItem(items[i], true, '', "javascript:" + actions[i], null, null, false,'lwpMenuItem', 'lwpSelectedMenuItem') );
			        } 
			        else {
			             menu.add( new UilMenuItem(items[i], true, '', actions[i], null, null, false,'lwpMenuItem', 'lwpSelectedMenuItem') );
			        }
			    }
			}
		}


	function registerInstantMessagingEvents() {
		if (typeof PeopleLinksAddListener == "function") {
			PeopleLinksAddListener("PeopleLinksLoggedIn", "PeopleLinksLoggedIn_PersonJS");
			PeopleLinksAddListener("PeopleLinksLoggedOut", "PeopleLinksLoggedOut_PersonJS");
			PeopleLinksAddListener("PeopleLinksUserStatusChanged", "PeopleLinksUserStatusChanged_PersonJS");
			PeopleLinksAddListener("PeopleLinksUsersStatusChanged", "PeopleLinksUsersStatusChanged_PersonJS");
		}
		else if (typeof STLinksAddListener == "function") {
			STLinksAddListener("STLinksLoggedIn", "STLinksLoggedIn_PersonJS");
			STLinksAddListener("STLinksLoggedOut", "STLinksLoggedOut_PersonJS");
			STLinksAddListener("STLinksUserStatusChanged", "STLinksUserStatusChanged_PersonJS");
		}
		else
		{
			// Avoids collision with the Sametime Contact List and WhoIsHere portlet's STLinksLoggedIn and 
			// STLinksUserStatusChanged functions
			wps_loggedInFuncs["STLinksLoggedIn_PersonJS"] = 0;
			wps_loggedOutFuncs["STLinksLoggedOut_PersonJS"] = 0;
			wps_userStatusFuncs["STLinksUserStatusChanged_PersonJS"] = 0;
		}
		registeredInstantMessagingEvents = true;
	}
		
		function STLinksLoggedIn(userId,displayName)
		{
			for (var funcName in wps_loggedInFuncs) 
				eval(funcName + '("' + escapeForJavaScript(userId) + '","' + escapeForJavaScript(displayName) + '")');
		}
		function STLinksLoggedOut(reason)
		{
			for (var funcName in wps_loggedOutFuncs) 
				eval(funcName + '("' + reason + '")');
		}

		function STLinksUserStatusChanged(userId,displayName,status,statusMsg,groupName)
		{
			statusMsg = escapeStatusMsg(statusMsg);
			for (var funcName in wps_userStatusFuncs) {
				if (funcName == "STLinksUserStatusChanged_PersonJS")
					eval(funcName + '("' + userId + '","' + displayName + '",' + status + ',"' + statusMsg + '","' + groupName + '")');
				else
					eval(funcName + '("' + escapeForJavaScript(userId) + '","' + escapeForJavaScript(displayName) + '",' + status + ',"' + statusMsg + '","' + groupName + '")');
			}
		}
		function STLinksUserEnteredPlace(id,name,placeId)
		{
			for (var funcName in wps_userEnteredFuncs) 
				eval(funcName + '("' + escapeForJavaScript(id) + '","' + escapeForJavaScript(name) + '","' + placeId + '")');
		}		

		function STLinksUserLeftPlace(id,name,placeId)
		{
			for (var funcName in wps_userLeftFuncs) 
				eval(funcName + '("' + escapeForJavaScript(id) + '","' + escapeForJavaScript(name) + '","' + placeId + '")');
		}		
		

		function PeopleLinksUsersStatusChanged_PersonJS( usersInfo, groupId ) {
			if (typeof (usersInfo) == "object")
			{
				if (typeof (groupId) == "undefined" )
					groupId = "";
		
				for ( var i = 0 ; i < usersInfo.length ; i++ )
				{
					PeopleLinksUserStatusChanged_PersonJS (usersInfo[i].usermail,
								usersInfo[i].userstatustype, 
								usersInfo[i].userstatusdescr,
								groupId);
				}
			}
		}
		
		function PeopleLinksUserStatusChanged_PersonJS( userid, status, statusmessage, groupId ) {
			status = status + "";
			awarenessController.updatePersonStatus(userid,status,statusmessage);
		}
		
		// this is the sametime controlling object and the funtion that st calls when events occurs
		function STLinksUserStatusChanged_PersonJS(userid, displayname, status, statusmessage, groupName){
			status = status + "";	//convert to a string, in case we are passed a number rather than a string
			awarenessController.updatePersonStatus(userid,status,statusmessage);
		}
	
		function STLinksLoggedOut_PersonJS(reason)
		{
			persontag_isLoggedIn = false;
			if (typeof awarenessController != "undefined" && awarenessController != null) {
				awarenessController.loggedOut();
			}
		}

		function PeopleLinksLoggedOut_PersonJS(userEmail, reason)
		{
			persontag_isLoggedIn = false;
			if (typeof awarenessController != "undefined" && awarenessController != null) {
				awarenessController.loggedOut();
			}
		}

		// Note:  assumes that the STLinks applet is written out in the PeopleEndTag at the
		//		bottom of the HTML page!  This way we avoid race conditions.
		function STLinksLoggedIn_PersonJS(myUserId, myUserName)
		{
			// addresses performance SPR YSAI5ZAQBP - Multiple "WatchUsers" requests from the person tag
			if (persontag_isLoggedIn == true)
				return;

			persontag_isLoggedIn = true;

			// register the names again with ST, in case we get logged out then back in
			sendNamesToInstantMessaging(personjsSTLinksWatchNames, true);
		}

		function PeopleLinksLoggedIn_PersonJS(myUserEmail)
		{
			// addresses performance SPR YSAI5ZAQBP - Multiple "WatchUsers" requests from the person tag
			if (persontag_isLoggedIn == true)
				return;

			persontag_isLoggedIn = true;

			// register the names again with ST, in case we get logged out then back in
			sendNamesToInstantMessaging(personjsSTLinksWatchNames, true);
		}

		function sendNamesToInstantMessaging(names, force) {
			if (names == null || names.length < 1)
				return;


			// Only call watchUsers if we are logged in!
			// Used force because the ordering of event listeners may not be predictable, and could influence whether
			// ll_loggedIn or g_fIsLoggedIn has been set yet.
			if (force ||
				(typeof ll_loggedIn != "undefined" && ll_loggedIn == true) ||
				(typeof g_fIsLoggedIn != "undefined" && g_fIsLoggedIn == true)) {

				if (typeof PeopleLinksWatchUsers != "undefined") {
					PeopleLinksWatchUsers(names);
				}
				else if (typeof STLinksWatchUsers != "undefined") {
					STLinksWatchUsers(names, true);
				}
			}
		}


function hexString (num, wid)
{
   var str = "";
   var digit = 0;
   var hexDigits = "0123456789ABCDEF";

   while (num > 0)
   {
      digit = num % 16;
      str = hexDigits.charAt(digit) + str;
      num >>= 4;
   }

   while (str.length < wid)
      str = "0" + str;

   return str;
}

function convToUtf8 (unival)
{
	var utf8 = "";

	if (unival <= 0x7f)
	{
		if( unival == 0x20 ) {
			utf8 = "+";
		}
		else if( (unival >=0x30 && unival <=0x39) || 
				 (unival >= 0x41 && unival <= 0x5a) ||
				 (unival >= 0x61 && unival <= 0x7a ) || 
				 (unival == 0x2A) || 
				 (unival == 0x2D ) ||
				 (unival == 0x2E ) ||
				 (unival == 0x5F ) ||
				 (unival == 0x3A) || 
				 (unival == 0x2F ) 				 
				 ) {
			utf8 = String.fromCharCode(unival);
		}
		else {
			utf8 = "%" + hexString( unival, 2 );
		}
	}

	else if (unival <= 0x07FF)
	{
	   utf8 = "%" + hexString(0xC0 + (unival >> 6), 2);
	   utf8 += "%" + hexString(0x80 + (unival & 0x3F), 2);
	}

	else if (unival <= 0xFFFF)
	{
	   utf8 = "%" + hexString(0xE0 + (unival >> 12), 2);
	   utf8 += "%" + hexString(0x80 + ((unival >> 6) & 0x3F), 2);
	   utf8 += "%" + hexString(0x80 + (unival & 0x3F), 2);
	}

	else if (unival <= 0x10FFFF)
	{
	   utf8 = "%" + hexString(0xF0 + (unival >> 18), 2);
	   utf8 += "%" + hexString(0x80 + ((unival >> 12) & 0x3F), 2);
	   utf8 += "%" + hexString(0x80 + ((unival >> 6) & 0x3F), 2);
	   utf8 += "%" + hexString(0x80 + (unival & 0x3F), 2);
	}

	return utf8;
}        

function URLEncoder( sstr ) {
	var dstr = "";
	var value = 0;

	for(var ix = 0; ix < sstr.length; ix++ )
	{
		value = sstr.charCodeAt(ix);
		dstr += convToUtf8( value );
	}
	return dstr;
}

//Converts this:  I am "Active
//to:             I am \"Active
function escapeStatusMsg(str)
{
	var retStr = str;
	var loc = 0;
	
	while ((loc = retStr.indexOf("\"", loc)) >= 0)
	{
		retStr = retStr.substring(0, loc) + "\\" + retStr.substring(loc);
		loc += 2;
	}
	
	return retStr;
}

//Converts this:  Scott O\'Reilly  or Scott O\"Reilly
//to:             Scott O\\'Reilly or Scott O\\"Reilly
function escapeForJavaScript(str)
{
	var retStr = str;
	var loc = 0;
	
	while ((loc = retStr.indexOf("\\", loc)) >= 0)
	{
		retStr = retStr.substring(0, loc)+"\\"+retStr.substring(loc);
		loc += 2;
	}
	
	return retStr;
}

// Converts a distinguished name like this "CN=John, Smith,OU=CAM,O=Lotus" to "CN=John\, Smith,OU=CAM,O=Lotus"
// It doesn't convert common names, however, because it would violate RFC 2254.
// Note that it only escapes the comma if it's in the first part of the heirarchy.
function escapeSlashComma(str)
{	
	var commaIndex = str.indexOf(",");
	if (commaIndex >= 0)
	{
		var eqIndex = str.indexOf("=");
		// no equal sign (indicates a common name, ie.  Collins, Chris)
		if (eqIndex < 0)
		{
			// DON'T ESCAPE COMMON NAMES 
			// only 1 comma
			//if (commaIndex == str.lastIndexOf(","))
			//	return escapeComma(str);
		}
		else
		{
			var eqIndex2 = str.indexOf("=", eqIndex+1);
			// no second equal
			if (eqIndex2 < 0)
				return escapeComma(str);
			else
			{
				// go backwards from second equal until I find either a slash or a comma
				for (var x=eqIndex2 - 1; x >= 0; x--)
				{
					if (str.charAt(x) == ',' || str.charAt(x) == '/')
						return escapeComma(str.substring(0, x)) + str.substring(x, str.length);
				}
			}
		}
	}
	return str;
}

//helper method for use by escapeSlashComma only
function escapeComma(str) 
{
	var commaIndex = str.indexOf(",");
	if (commaIndex >= 0)
	{
		// (assumes only 1 comma in the string)
		if (str.charAt(commaIndex-1) != '\\')
			return str.substring(0, commaIndex) + '\\' + str.substring(commaIndex, str.length);
	}
	return str;
}
	
	function handleChatPersonMenuItem(email){
		if (typeof STLinksCreateIM == "function"){
			STLinksCreateIM(escapeSlashComma(email));
		}
	}
	
	function portalShowProfile(profileURL){
		window.open(profileURL);
	}
	
	function portalFindDocs(kmapURL){
		window.open(kmapURL);
	}

	function portalShowAddToContactsUI(name){
		linkId = name;
		PAshowAddToContactsUI(null, null);
	}

// we initialize a new instance of the person controller
var awarenessController = new AwarenessController();


// This class represents the business card entries used in the person tag
// stores displayName, and an array of data associated with the business
// card section. 
// also has useful helper functions to see if person has these attributes
function BusinessCard(){
		
	var displayName = '';
	var cardData = '';
	
	function setVariables(displayName, cardData) {
		this.displayName = displayName;
		this.cardData = cardData;
	}

	function getDisplayName(){
		return this.displayName;
	}
	
	function getCardData(){
		return this.cardData;
	}
	
	

	function hasCardData() {
		if (this.cardData == '') {
			return false;
		} else {
			return true;
		}
	}

	
	this.setVariables = setVariables;
	
	this.getDisplayName = getDisplayName;
	this.getCardData = getCardData;

	this.hasCardData = hasCardData;
}

// this class represents an individual menuitem used in the person tag
// it stores the menuitem's displayName, uri, and wether or not the menu item is sensitve to a 
// person's online status
function MenuItem() {
	var displayName = '';
	var uri = '';
	var awareness = false; // variable to indicate if menu item is sensitive to someone's online status

	function setVariables(displayName, uri, awareness) {
		this.displayName = displayName;
		this.uri = uri;
		this.awareness = awareness;
	}

	function getDisplayName() {
		return this.displayName;
	}

	function getURI() {
		return this.uri;
	}

	function isAwarenessSensitive() {
		return this.awareness;
	}

	this.setVariables = setVariables;
	
	this.getDisplayName = getDisplayName;
	this.getURI = getURI;
	
	this.isAwarenessSensitive = isAwarenessSensitive;		
}

// this function generates the WCL popup menu code based upon the businesscard entries, and
// menus stored in the menu list, it also takes in a target inorder for WCL to work on Mozilla/Netscape
function buildMenu(userID, menuList, target) {
	
	// retrieve the person's business card object
	var businessCard = awarenessController.getBusinessCardForUser(userID);

	// check to see if user is really online (active or away)
	var status = awarenessController.getOnlineStatusForUser(userID);
	
	// generates a new menuID each time so we don't get cached results
	var menuID = userID + awarenessController.getRandomSeed(); 

	// gets BIDI info from awarenessController 
	var isLTR = awarenessController.getLTR();

	// creates the actual wcl menu	
	var menu = createContextMenu( menuID, isLTR);
	
	// used to determine if we should show a sperator between the business card entries and 
	// the menuItems
	var showEndSeperator = false;
						
	
	// adds the displayName entry for the menu, uses special style for first menu entry		
	menu.add( new UilMenuItem(businessCard.getDisplayName(), false, true, null, 
		null, null, true, 'lwpMenuHeader','lwpSelectedMenuItem'));

	menu.add( new UilMenuItem('', false, null, null, 
			null, null, false, 'lwpMenuHeader','lwpSelectedMenuItem'));

	// if the user has has business card data, we iterate through the values, and
	// display it in the business card section of the menu
	if (businessCard.hasCardData()) {
		var cardData = businessCard.getCardData();
		var length = cardData.length;
		for (var i=0; i < length; i++) {
			if (cardData[i] != "") {
				showEndSeperator = true;
				menu.add( new UilMenuItem(cardData[i], false, null, null, 
				null, null, false, 'lwpMenuHeader','lwpSelectedMenuItem'));	
			}
		}
	}	
	
	// if there are actual entries in the business card section, we will add another seperator
	// to seperate it from the user status
	if (showEndSeperator) {
		menu.add( new UilMenuItem('', false, null, null, 
			null, null, false, 'lwpMenuHeader','lwpSelectedMenuItem'));	
	}
	

	// we will attempt to get their status message from sametime
	var statusMessage = awarenessController.getStatusMessageForUser(userID);
	menu.add( new UilMenuItem(statusMessage, false, null, null, 
		null, null, false, 'lwpMenuHeader','lwpSelectedMenuItem')); 
	
	menu.addSeparator();
	

	

	// iterate through the menu items and builds the appropriate WCL menu items for them
	for (var i=0;i < menuList.length;i++){
		if (menuList[i].isAwarenessSensitive()) {
			if (status) {
				menu.add( new UilMenuItem(menuList[i].getDisplayName(), true, '', 
					menuList[i].getURI(), null, null, false,'lwpMenuItem', 'lwpSelectedMenuItem'));
			}
		} else {					
			menu.add( new UilMenuItem(menuList[i].getDisplayName(), true, '', 
				menuList[i].getURI(), null, null, false,'lwpMenuItem', 'lwpSelectedMenuItem'));
		}
	}

	// in case users have added more menu items, we must add them to the list of official menu items
	if (typeof addPeopleMenuMoreMenuItems != "undefined") {
		addPeopleMenuMoreMenuItems(menu);
	}

	// shows the fully generated popup menu
	return showContextMenu(menuID, target); 
}

// function for taking an id of an <img> html tag and converting it to the down arrow gif
function swapDownImg(id) {
	var elem = document.getElementById(id);
	if (elem != null) 
		elem.src = awarenessController.getMenuDropdownIconPath();
}

// function for taking an id of an <img> html tag and converting it to the clear image gif
function swapClearImg(id) {
	var elem = document.getElementById(id);
	if (elem != null) 
		elem.src = awarenessController.getClearPixelIconPath();
}

// This class is a collection of helper functions to manage all the PersonObjects on the page
function AwarenessController() {
	var ICON_MENUDROPDOWN = '';
	var ICON_CLEARPIXEL = '';
	var ICON_STATUSACTIVE = '';
	var ICON_STATUSAWAY = '';
	var ICON_STATUSDONOTDISTURB = '';
	var ICON_STATUSMOBILE = '';
	
	var randomSeed = 1; // seed used to help make WCL menus generate a random menuID		
	var isLTR = true; // boolean var
	var activeMsg = ''; // active message
	var awayMsg = ''; // away message
	var dndMsg = '';	// do not disturb message
	var unAvailableMsg = ''; // unavailable message
	var offlineMsg = ''; // offline message
	var clickForOptions = ''; // message for click for options
	var personArray = new Object(); // associative array which stores all the PersonObjects

	// This is a helper function which creates a new PersonObject for a user
	// as well as register thems with ST links
	function registerPerson(displayName, userID, cardData,counter,leftAligned) {

		if (!registeredInstantMessagingEvents) {
			registerInstantMessagingEvents();
		}

		// we derive the various IDs from the passed in counter
		var linkID = "menu-link-" + counter;
		var statusImgID = "status-img-" + counter;

		// in case where the user doesn't have an email, we use the displayName as
		// the userID
		if (userID == '') {
			userID = displayName;
		}

		// case where we have a new user
		if (personArray[userID] == null) {

			//register person with STlinks
			registerSTWatch(userID);

			// create a new PersonObject and populate it's variables
			var person = new PersonObject();
			person.setStatusMessage(this.unAvailableMsg);
			person.setLinkID(linkID);
			if (leftAligned == true) {
				person.setLeftAlignedImageID(statusImgID);
			} else {
				person.setStatusImageID(statusImgID);
			}

			var businessCard = new BusinessCard();
			businessCard.setVariables(displayName, cardData);
			person.setBusinessCard(businessCard);

			// add PersonObject to internal PersonObjects array
			personArray[userID] = person;
		} else {

			// case where user is already in the PersonObjects array
			// we simply add the new ids associated with the user
			personArray[userID].setLinkID(linkID);
			if (leftAligned == true) {
				personArray[userID].setLeftAlignedImageID(statusImgID);
			} else {
				personArray[userID].setStatusImageID(statusImgID);
			}
			personArray[userID].update();
		}
	}

	// this function is called by ST links to update us on the status of the user
	function updatePersonStatus(userID, status, statusMessage) {

		//ignore case of passed-in name
		var key;
		userID = userID.toLowerCase();
		for (key in personArray) {
			if (key.toLowerCase() == userID) {
				userID = key;
			}
		}

		// we retrieve the PersonObject associated with the user
		var person = personArray[userID];

		// case where user does not exist on the system
		if (typeof person == "undefined" || person == null)
			return;	
		
		// situation in which no status message is returned to us from ST Links (occurs when
		// user has not changed their default message)
		// in this case we derive what the default status message should be for any given status
		if (statusMessage == null || statusMessage.length < 1) {
			switch (status) {
				case "0":        //offline 								
	    				statusMessage = this.offlineMsg.replace(/%s/gi, this.getDisplayNameForUser(userID));
					break;
				case "32":        //active
    					statusMessage = this.activeMsg;
					break;
				case "64":        //not using computer
    					statusMessage = this.awayMsg;
					break;
				case "96":        //away
    					statusMessage = this.awayMsg;
					break;
				case "128":        //do not disturb
    					statusMessage = this.dndMsg;
					break;
				case "544":        //mobile active
    					statusMessage = this.activeMsg;
					break;
				case "608":        //mobile away
    					statusMessage = this.awayMsg;
					break;
				default:
					statusMessage = this.offlineMsg;
				}
		}	

		// update the PersonObject with the new info
		person.setStatus(status);
		person.setStatusMessage(statusMessage);

		// tell the PersonObject to update all the person tag links on the
		// page with the appropriate information
		person.update();
	}
	
	// Update each person in the UI to reflect offline status
	function loggedOut() {
		var key;
		for (key in personArray) {
				updatePersonStatus(key, "", this.unAvailableMsg);
		}
	}

	// sets the various status messages	
	function setMessages(activeMsg, awayMsg, dndMsg, unAvailableMsg, offlineMsg, clickForOptions) {
		this.activeMsg = activeMsg;
		this.awayMsg = awayMsg;
		this.dndMsg = dndMsg;
		this.unAvailableMsg = unAvailableMsg;
		this.offlineMsg = offlineMsg;
		this.clickForOptions = clickForOptions;
	}
 
	// sets the BIDI values
	function setLTR(LTR) {
		isLTR = LTR;
	}

	// OBSOLETE - we no longer use the peopleInit tag to set the directory.  Instead, the person tag
	// sets it using setIconPaths()
	// sets the WPS icon directory
	function setIconDirectory(iconDirectory) {
	}

	// dummy function for the old dynamic person tag
	function setDynamicPTagServletName(name) {
	}

	// private method that registers user watches with STLinks
	function registerSTWatch(userName){
		if (userName == null || userName.length < 1)
			return;

		userName = escapeSlashComma(userName);
		
		// save the names in case we get logged out, then logged back in
		personjsSTLinksWatchNames += (personjsSTLinksWatchNames.length > 0 ? ";" : "") + userName;

		sendNamesToInstantMessaging(userName, false);
	}

	// Returns the person object associated with the userID
	function getPersonObject(userID) {
		return personArray[userID];
	}

	// retrieves the string click for options
	function getClickForOptions() {
		return this.clickForOptions;
	}

	// get the BIDI value
	function getLTR() {
		return(isLTR);			
	}

	// this function returns the random seed to make WCL menus generate a random menuID
	function getRandomSeed() {
		return randomSeed++;
	}

		
	// this get status message function returns a string which does not contain the words click for option
	function getStatusMessageForUser(userID){
		var statusMessage = this.unAvailableMsg;
		if (personArray[userID] != null) {
			statusMessage = personArray[userID].getStatusMessage();	
		}

		return statusMessage;			
	}

	// This function determines the online status of a user
	// will only return true if the person is active, or away
	function getOnlineStatusForUser(userID){
		switch (personArray[userID].getStatus()){
			case "32":
			case "96":
			case "544":
			case "608":
				return true;
			case "0":
			case "64":
			case "128":
				return false;
		}
		return false;					
	}

	// Returns the displayName associated with a userID
	function getDisplayNameForUser(userID) {
		var businessCard = personArray[userID].getBusinessCard();
		return businessCard.getDisplayName();
	}

	// Returns the businessCard associated with a userID
	function getBusinessCardForUser(userID) {
		var businessCard = personArray[userID].getBusinessCard();
		return businessCard;
	}

	// creates the <img src HTML that embodies the status image icon
	function createStatusImage(userID, counter) {
		var output ="<img id='status-img-"+ counter +"' ALT=\"" + this.getStatusMessageForUser(userID) + "\" src='";
		output += ICON_CLEARPIXEL + "' width='16' height='15' border='0' align='absmiddle'>";
		return output;
	}

	// creates the <img src HTML that embodies the down image icon
	function createDownImage(counter) {
		var output = "<img id='down-img-" + counter + "'ALT=\"" + this.clickForOptions + "\"";
		output += " src='" + ICON_CLEARPIXEL + "' width='16' height='16' border='0' align='absmiddle'>";
		return output;
	}
	
	// creates the <a href HTML that embodies the link to enable the menu
	// !!!!!!!THIS FUNCTION DOES NOT INCLUDE THE "</a>" TO END THE HREF, PLEASE ADD THAT TO THE END OF VAR !!!!!!!!!!!!!!!!!!!!!!!
	function createMenuLink(userID, counter) {
		var output = "<a id='menu-link-"+ counter +"' title='" + this.getStatusMessageForUser(userID);
		output += ' ' + this.clickForOptions + "' href='#' class='wpsPersonName'";
			
		output += " onMouseOver=\"swapDownImg('down-img-" + counter + "');\"";
		output += " onMouseOut=\"swapClearImg('down-img-" + counter + "');\"";
		output += "onclick=\"createMenuData" + counter + "(event.target);  return false;\">";

		return output;		
	}

	// this function brings together the various components that make up a person tag link (<status img>displayName<down img>).
	function writeOutPerson(userID, counter) {
		var displayName = this.getDisplayNameForUser(userID);
		var statusImage = this.createStatusImage(userID, counter);
		var downImage = this.createDownImage(counter);
		var menuLink = this.createMenuLink(userID, counter);
	
		// The format of a standard person tag link is <status img>displayName<down img>, keep in mind the status 
		// img and down img is wrapped the displayName, which is just an HTML link	
		var output = menuLink + statusImage + displayName + downImage + "</a>";		
		return output;
	}
	function setDynamicPTagServletBaseURL(junk) {
	}

	function setIconPaths(MENUDROPDOWN, CLEARPIXEL, STATUSACTIVE, STATUSAWAY, STATUSDONOTDISTURB, STATUSMOBILE) {
		this.ICON_MENUDROPDOWN = MENUDROPDOWN;
		this.ICON_CLEARPIXEL = CLEARPIXEL;
		this.ICON_STATUSACTIVE = STATUSACTIVE;
		this.ICON_STATUSAWAY = STATUSAWAY;
		this.ICON_STATUSDONOTDISTURB = STATUSDONOTDISTURB;
		this.ICON_STATUSMOBILE = STATUSMOBILE;
	}
	function getMenuDropdownIconPath() {
		return this.ICON_MENUDROPDOWN;
	}
	function getClearPixelIconPath() {
		return this.ICON_CLEARPIXEL;
	}
	function getStatusActiveIconPath() {
		return this.ICON_STATUSACTIVE;
	}
	function getStatusAwayIconPath() {
		return this.ICON_STATUSAWAY;
	}
	function getStatusDoNotDisturbIconPath() {
		return this.ICON_STATUSDONOTDISTURB;
	}
	function getStatusMobileIconPath() {
		return this.ICON_STATUSMOBILE;
	}

	this.setIconDirectory = setIconDirectory;	
	this.setLTR = setLTR;
	this.setMessages = setMessages;
      this.setDynamicPTagServletName = setDynamicPTagServletName;

	this.getPersonObject = getPersonObject;
	this.getStatusMessageForUser = getStatusMessageForUser;
	this.getLTR = getLTR;
	this.getRandomSeed = getRandomSeed;
	this.getClickForOptions = getClickForOptions;
	this.getOnlineStatusForUser = getOnlineStatusForUser;
	this.getDisplayNameForUser = getDisplayNameForUser;
	this.getBusinessCardForUser = getBusinessCardForUser;

	this.registerPerson = registerPerson;
	this.updatePersonStatus = updatePersonStatus;
	this.writeOutPerson = writeOutPerson;
	this.createStatusImage = createStatusImage;
	this.createDownImage = createDownImage;
	this.createMenuLink = createMenuLink;
	this.setDynamicPTagServletBaseURL = setDynamicPTagServletBaseURL;
	this.loggedOut = loggedOut;

	this.setIconPaths = setIconPaths;
	this.getMenuDropdownIconPath = getMenuDropdownIconPath;
	this.getClearPixelIconPath = getClearPixelIconPath;
	this.getStatusActiveIconPath = getStatusActiveIconPath;
	this.getStatusAwayIconPath = getStatusAwayIconPath;
	this.getStatusDoNotDisturbIconPath = getStatusDoNotDisturbIconPath;
	this.getStatusMobileIconPath = getStatusMobileIconPath;
}

// this class represents a unique person on the page, and stores their status, status message, as
// well as the all the IDs for every link/img associated with them.  
function PersonObject(){
	var status = ''; // ST status of user
	var statusMessage = ''; // ST status message
	var statusImageIDArray = new Array(); // Array of IDs for the status image of a person
	var linkIDArray = new Array(); // Array of IDs for the link to create a menu for a person
	var businessCard; // Object holding the business card data for the user
	// Array of IDs for status images of a person that is left aligned
	var leftAlignedImageIDArray = new Array(); 
	// this function sets the business card object associated with this user
	function setBusinessCard(card) {
		this.businessCard = card;
	}	

	// this function sets the ID for the status image so that we can later modify it's src to adjust
	// for the different imgs associated with each status
	function setStatusImageID(id){
		statusImageIDArray[statusImageIDArray.length] = id;
	}

	// this function sets the ID for the status images of left
	// aligned images so that we can later modify it's src to adjust
	// for the different imgs associated with each status
	function setLeftAlignedImageID(id){
		leftAlignedImageIDArray[leftAlignedImageIDArray.length] = id;
	}

	// this function sets the ID for the link to open the person tag so that we can later modify it's title to adjust
	// for the different status messages associated with each status
	function setLinkID(id){
		linkIDArray[linkIDArray.length] = id;
	}

	// sets the ST status of the person
	function setStatus(status){		
		this.status = status;
	}
		
	// sets the current status message for the user
	function setStatusMessage(statusMessage){
		
		// we check to see if the message has a period at the end, we add it if it doesn't have one
		if (statusMessage != null && statusMessage.length > 0) {
			if (statusMessage.charAt(statusMessage.length-1) != '.') 
					statusMessage += '.';
			}
		this.statusMessage = statusMessage;
	}
	
	// returns the business card associated with this person
	function getBusinessCard() {
		return this.businessCard;
	}

	// returns the status message of the current user
	function getStatusMessage(){
		return this.statusMessage;
	}
		
	// returns the status of the current user
	function getStatus(){
		return this.status;
	}

	// this function updates the link and status image of the person with the right info
	function update() {
		// for all the links associated with the person, we update the title to have
		// the correct status message
		for (var i=0; i < linkIDArray.length;i++) {
			var elem = document.getElementById(linkIDArray[i]);
			if (elem != null) 
				elem.title = this.statusMessage + ' ' + awarenessController.getClickForOptions();
		}

		// for all the status images associated with the person, we update the img and alt
		// text messages to match the user's current status
		for (var i=0; i < statusImageIDArray.length;i++) {
			var elem = document.getElementById(statusImageIDArray[i]);
			if (elem != null) {
				elem.alt = this.statusMessage;
				elem.src = this.getStatusIcon();
			}
		}

		// for all the status images associated with the person that is left alligned, 
		// we update the img and alt text messages to match the user's current status
		// like any other status image.  However, if they are offline we set the width
		// and height to 0 to hide them
		for (var i=0; i < leftAlignedImageIDArray.length;i++) {
			var elem = document.getElementById(leftAlignedImageIDArray[i]);
			if (elem != null) {				
				if (this.status == null || this.status == "0") {
					elem.width='0';
					elem.height='0';	
				} else {
					elem.width='16';
					elem.height='16';	
				}
				elem.alt = this.statusMessage;
				elem.src = this.getStatusIcon();
			}
		}
	}
	
	// function for deriving what status icon should go with each ST status
	function getStatusIcon(){
			switch (this.status){
				case "0":        //offline 
    					return awarenessController.getClearPixelIconPath();
					break;
				case "32":        //active
    					return awarenessController.getStatusActiveIconPath();
					break;
				case "64":        //not using computer
    					return awarenessController.getStatusAwayIconPath();
					break;
				case "96":        //away
					return awarenessController.getStatusAwayIconPath();
					break;
				case "128":        //do not disturb
					return awarenessController.getStatusDoNotDisturbIconPath();
					break;
				case "544":        //mobile active
					return awarenessController.getStatusMobileIconPath();
					break;
				case "608":        //mobile away
					return awarenessController.getStatusAwayIconPath();  
					break;
			}
			return awarenessController.getClearPixelIconPath();		
		}


	this.setBusinessCard = setBusinessCard;
	this.setStatusImageID = setStatusImageID;
	this.setLeftAlignedImageID = setLeftAlignedImageID;
	this.setLinkID = setLinkID;	
	this.setStatus = setStatus;
	this.setStatusMessage = setStatusMessage;
	
	this.getBusinessCard = getBusinessCard;
	this.getStatus = getStatus;
	this.getStatusMessage = getStatusMessage;
	this.getStatusIcon = getStatusIcon;
	this.update = update;
}

function invokeAction(formName, actionRef){
	var array = new Array(1);
	array[0] = actionRef;
	c2a_invokeMenuAction(formName,array);	
}
