	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 = null;	
	var registeredInstantMessagingEvents = false;	
	var personjsSTLinksWatchNames = "";
	var persontag_isLoggedIn = false;
	var persontag_awarenessversion = ""; // indicates whether we are using classic ST or SIP IM

	function setDefaultDynamicPersonTagUrl(url) {
		defaultDynamicPersonTagURL = url;
	}

	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;
	}	
	
	function invokePersonTagMenu(counter, target) {
		// get data for the user
		var menuDataTagObject = awarenessController.getMenuDataTagObject(counter);

		var menuData = menuDataTagObject.getMenuData();

		if (menuData == null) {
		var userId = menuDataTagObject.getUserId();
		var userIdType = menuDataTagObject.getUserIdType();
		var contextArray = menuDataTagObject.getContextArray();		
		var theHTML = "";
		var index;
		
		var 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;
		
		
		// indicate we are retrieving just data
		params += "&getMenuData=TRUE";

		// Add the other params to the URL
		if (typeof contextArray != "undefined" && contextArray != null && contextArray != "") {
			var contextValue = "";
			for (var item in contextArray) {
				if (contextArray[item] != "undefined" && contextArray[item] != null && contextArray[item] != "") {
					contextValue += URLEncoder(item) + URLEncoder(",") + URLEncoder(contextArray[item]) + URLEncoder(";");
				}
			}
			params += "&contextArray=" + contextValue;
		}
		
		//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);
		menuData = maindata;
		menuDataTagObject.setMenuData(menuData);
		} 
							
		eval(menuData);
		var IMLogin = menuDataTagObject.getIMLogin();
		var linkDisplayName = menuDataTagObject.getDisplayName();
		var businessCard = new BusinessCard();
		
		var menuDisplayName = displayName;
		if (displayName=='') {
			menuDisplayName = linkDisplayName;
		}		
		businessCard.setVariables(menuDisplayName,cardData);

		return(buildMenu(IMLogin,menuList,businessCard,target));
		
	}
	//	
	// javascript:invokeDynamicPersonTag("CN=wpsadmin,O=eCAT", "LDAPDN", "wpsadmin")
	//
	function invokeDynamicPersonTag (userId, userIdType, displayName, IMLogin, contextArray) {
					
		// 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";
		}

		
		return(awarenessController.writeOutLink(userId,userIdType,displayName,IMLogin,false,contextArray));
	}

	// 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, IMLogins, contextArray) {
	
						
		// Must have a userIds Array
		if (typeof userIds != "object" || userIds == null || userIds == "") {
			return;
		}

		var writeOutPersonCallArray = new Array();
							
		for (var x=0; x < userIds.length; x++) {					
			writeOutPersonCallArray[x] = awarenessController.writeOutLink(userIds[x],userIdTypes[x],displayNames[x],IMLogins[x],false,contextArray);
		}

		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 (persontag_awarenessversion == "classic") {
			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;
			}
		}
		else {
			if (typeof PeopleLinksAddListener == "function") {
				PeopleLinksAddListener("PeopleLinksLoggedIn", "PeopleLinksLoggedIn_PersonJS");
				PeopleLinksAddListener("PeopleLinksLoggedOut", "PeopleLinksLoggedOut_PersonJS");
				PeopleLinksAddListener("PeopleLinksUserStatusChanged", "PeopleLinksUserStatusChanged_PersonJS");
				PeopleLinksAddListener("PeopleLinksUsersStatusChanged", "PeopleLinksUsersStatusChanged_PersonJS");
			}
		}
		registeredInstantMessagingEvents = true;
	}
		
		function STLinksLoggedIn(userId,displayName)
		{
			for (var funcName in wps_loggedInFuncs) 
				eval(funcName + '("' + userId + '","' + displayName + '")');
		}
		function STLinksLoggedOut(reason)
		{
			for (var funcName in wps_loggedOutFuncs) 
				eval(funcName + '("' + reason + '")');
		}
		function STLinksUserStatusChanged(userId,displayName,status,statusMsg,groupName)
		{
			for (var funcName in wps_userStatusFuncs) {
				if (funcName == "STLinksUserStatusChanged_PersonJS")
					eval(funcName + '("' + userId + '","' + displayName + '",' + status + ',"' + statusMsg + '","' + groupName + '")');
				else
					eval(funcName + '("' + escapeForJavaScript(userId) + '","' + displayName + '",' + status + ',"' + statusMsg + '","' + groupName + '")');
			}
		}		

		function STLinksUserEnteredPlace(id,name,placeId)
		{
			for (var funcName in wps_userEnteredFuncs) 
				eval(funcName + '("' + id + '","' + name + '","' + placeId + '")');
		}		

		function STLinksUserLeftPlace(id,name,placeId)
		{
			for (var funcName in wps_userLeftFuncs) 
				eval(funcName + '("' + id + '","' + 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].usermail,
								usersInfo[i].userstatustype, 
								usersInfo[i].userstatusdescr,
								groupId);
				}
			}
		}
		
		function PeopleLinksUserStatusChanged_PersonJS( userid, displayName,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 (persontag_awarenessversion == "classic") {
				if (force || (typeof ll_loggedIn != "undefined" && ll_loggedIn == true)) {
					if (typeof STLinksWatchUsers != "undefined") {
						STLinksWatchUsers(names, true);
					}
				}
			}
			else {
				if (force || (typeof g_fIsLoggedIn != "undefined" && g_fIsLoggedIn == true)) {
					if (typeof PeopleLinksWatchUsers != "undefined") {
						PeopleLinksWatchUsers(names);
					}
				}
			}
		}


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;
}
	
	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, businessCard, 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 iconDirectory = awarenessController.getIconDirectory(); 
	iconDirectory += 'MenuDropdown.gif';
	var elem = document.getElementById(id);
	if (elem != null) 
		elem.src = iconDirectory;
}

// function for taking an id of an <img> html tag and converting it to the clear image gif
function swapClearImg(id) {
	var iconDirectory = awarenessController.getIconDirectory(); 
	iconDirectory += 'ClearPixel.gif';
	var elem = document.getElementById(id);
	if (elem != null) 
		elem.src = iconDirectory;
}

// This class is a collection of helper functions to manage all the PersonObjects on the page
function AwarenessController() {
	var randomSeed = 1; // seed used to help make WCL menus generate a random menuID
	var internalCounter = 1; // counter used to ensure every link has a unique id		
	var iconDirectory = '/wps/images/icons/'; // portal icon directory
	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
	var menuDataTagObjectArray = new Object(); // associative array which stores all the menuDataTagObjects
	
	// This is a helper function which creates a new PersonObject for a user
	// as well as register thems with ST links
	function registerPerson(userId,userIdType, displayName, IMLogin, counter,leftAligned,contextArray) {
		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 IMLogin
		if (IMLogin == '') {
			IMLogin = displayName;
		}

		// store data we will later need to access the menu data tag
		var menuDataTag = new MenuDataTagObject();

		//set ther UserId, userIdType, displayName,contextArray info
		menuDataTag.setIMLogin(IMLogin);
		menuDataTag.setUserId(userId);
		menuDataTag.setUserIdType(userIdType);
		menuDataTag.setDisplayName(displayName);			
		menuDataTag.setContextArray(contextArray);
		
		menuDataTagObjectArray[counter] = menuDataTag;
		
		// case where we have a new user
		if (personArray[IMLogin] == null) {
	
			//register person with STlinks
			registerSTWatch(IMLogin);

			// 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);
			businessCard.setVariables(displayName, null);
			person.setBusinessCard(businessCard);

			// add PersonObject to internal PersonObjects array
			personArray[IMLogin] = person;			
		} else {

			// case where user is already in the PersonObjects array
			// we simply add the new ids associated with the user
			personArray[IMLogin].setLinkID(linkID);
			if (leftAligned == true) {
				personArray[IMLogin].setLeftAlignedImageID(statusImgID);
			} else {
				personArray[IMLogin].setStatusImageID(statusImgID);
			}
			personArray[IMLogin].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.replace(/%s/gi, this.getDisplayNameForUser(userId));
				}
		}	

		// 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();
	}
	
	// this function is called by us to update the person link with the right default status messages.
	function updatePersonLink(userId) {

		//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;	
		
		// 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;
	}

	// sets the WPS icon directory
	function setIconDirectory(iconDirectory) {
		this.iconDirectory = 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];
	}

	// Returns the menuDataTagObject associated with the counter
	function getMenuDataTagObject(counter) {
		return menuDataTagObjectArray[counter];
	}

	// retrieves the string click for options
	function getClickForOptions() {
		return this.clickForOptions;
	}

	// this function returns the counter to ensure every link has a unique id
	function getCounter() {
		return internalCounter++;
	}

	// retrieves the WPS icon directory
	function getIconDirectory() {
		return this.iconDirectory;
	}	

	// 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,leftAligned) {
		var clearPixel = awarenessController.getIconDirectory(); 
		clearPixel += 'ClearPixel.gif';
		var size = '16';
		if (leftAligned) {
		    size = '0';
		}

		var output ="<img id='status-img-"+ counter +"' ALT=\"" + this.getStatusMessageForUser(userId) + "\" src='";
		output += clearPixel + "' width='" + size + "' height='" + size + "' border='0' align='absmiddle'>";
		return output;
	}

	// creates the <img src HTML that embodies the down image icon
	function createDownImage(counter) {
		var clearPixel = awarenessController.getIconDirectory(); 
		clearPixel += 'ClearPixel.gif';

		var output = "<img id='down-img-" + counter + "'ALT=\"" + this.clickForOptions + "\"";
		output += " src='" + 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, userIdType, displayName, IMLogin, counter) {
		var output = "<a id='menu-link-"+ counter +"' title=''" 
		output += " href='#' class='wpsPersonName'";
			
		output += " onMouseOver=\"swapDownImg('down-img-" + counter + "');\"";
		output += " onMouseOut=\"swapClearImg('down-img-" + counter + "');\"";
		output += "onclick=\"invokePersonTagMenu('" + 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 writeOutLink(userId,userIdType,displayName,IMLogin,leftAligned,contextArray) {
		var counter = this.getCounter();
		this.registerPerson(userId,userIdType,displayName,IMLogin,counter,leftAligned,contextArray);

		var statusImage = this.createStatusImage(IMLogin, counter,leftAligned);
		var downImage = this.createDownImage(counter);
		var menuLink = this.createMenuLink(userId,userIdType,displayName,IMLogin, 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 setAwarenessVersion(version) {
		persontag_awarenessversion = version;
	}

	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.getCounter = getCounter;
	this.getIconDirectory = getIconDirectory;
	this.getClickForOptions = getClickForOptions;
	this.getOnlineStatusForUser = getOnlineStatusForUser;
	this.getDisplayNameForUser = getDisplayNameForUser;
	this.getBusinessCardForUser = getBusinessCardForUser;
	this.getMenuDataTagObject = getMenuDataTagObject;

	this.registerPerson = registerPerson;
	this.updatePersonStatus = updatePersonStatus;
	this.updatePersonLink = updatePersonLink;
	this.writeOutLink = writeOutLink;
	this.createStatusImage = createStatusImage;
	this.createDownImage = createDownImage;
	this.createMenuLink = createMenuLink;
	this.setDynamicPTagServletBaseURL = setDynamicPTagServletBaseURL;
	this.loggedOut = loggedOut;
	this.setAwarenessVersion = setAwarenessVersion;
}

// 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){
		
		// just set status message, don't add period to it
		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 iconDirectory = awarenessController.getIconDirectory(); 
			iconDirectory += this.getStatusIcon();
			var elem = document.getElementById(statusImageIDArray[i]);
			if (elem != null) {
				elem.alt = this.statusMessage;
				elem.src = iconDirectory;
			}
		}

		// 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 iconDirectory = awarenessController.getIconDirectory(); 
			iconDirectory += this.getStatusIcon();
			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 = iconDirectory;
			}
		}
	}
	
	// function for deriving what status icon should go with each ST status
	function getStatusIcon(){
			switch (this.status){
				case "0":        //offline 
    					return 'ClearPixel.gif';
					break;
				case "32":        //active
    					return 'StatusActive.gif';
					break;
				case "64":        //not using computer
    					return 'StatusAway.gif'; // 
					break;
				case "96":        //away
					return 'StatusAway.gif';
					break;
				case "128":        //do not disturb
					return 'StatusDoNotDisturb.gif';
					break;
				case "544":        //mobile active
					return 'StatusMobile.gif';
					break;
				case "608":        //mobile away
					return 'StatusAway.gif';  
					break;
			}
			return 'ClearPixel.gif';		
		}


	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;
	
}

// this data object holds data we need to log onto the 
// the menu data tag
function MenuDataTagObject() {
	var IMLogin; // the login to get onto IM
	var userId; // the userId used to get data from WMM
	var userIdType; // the value 	
	var displayName; // the displayName used in the person tag link
	var menuData = null; // the data associated with the menu items + business card
	var contextArray = null; // stores page context info for menu items that need it

	// sets the userId to get data from WMM
	function setIMLogin(IMLogin){		
		this.IMLogin = IMLogin;
	}

	// sets the userId to get data from WMM
	function setUserId(userId){		
		this.userId = userId;
	}

	// sets the userId type to get data from WMM
	function setUserIdType(userIdType){		
		this.userIdType = userIdType;
	}

	// sets the display name used in the HTML link
	function setDisplayName(displayName){		
		this.displayName = displayName;
	}

	// sets the menu data for the menu items
	function setMenuData(menuData){		
		this.menuData = menuData;
	}

	// sets the context array
	function setContextArray(contextArray) {
		this.contextArray = contextArray;
	}

	// gets the userId to get data from WMM
	function getIMLogin(){		
		return(this.IMLogin);
	}

	// gets the userId to get data from WMM
	function getUserId(){		
		return(this.userId);
	}

	// gets the userId type to get data from WMM
	function getUserIdType(){		
		return(this.userIdType);
	}

	// gets the display name used in the HTML link
	function getDisplayName(){		
		return(this.displayName);
	}

	// gets the menu data for the menu items
	function getMenuData(){		
		return(this.menuData);
	}

	// gets the context array
	function getContextArray() {
		return(this.contextArray);
	}

	this.setIMLogin = setIMLogin;
	this.setUserId = setUserId;
	this.setUserIdType = setUserIdType;
	this.setDisplayName = setDisplayName;
	this.setMenuData = setMenuData;
	this.setContextArray = setContextArray;

	this.getIMLogin = getIMLogin;
	this.getUserId = getUserId;
	this.getUserIdType = getUserIdType;
	this.getDisplayName = getDisplayName;
	this.getMenuData = getMenuData;
	this.getContextArray = getContextArray;
}

function invokeAction(formName, actionRef){
	var array = new Array(1);
	array[0] = actionRef;
	c2a_invokeMenuAction(formName,array);	
}

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 name like this "CN=Scott, Smith,OU=CAM,O=Lotus" to "CN=Scott\, Smith,OU=CAM,O=Lotus"
// Note that it only escapes the comma if it's in the first part of the heirarchy.
function escapeSlashComma(str)
{
	var index = str.indexOf("=");
	var subStr = str.substring(index+1, str.length);
	var cnStr = subStr.substring(0, subStr.indexOf("="));
	
	var indexComma = str.indexOf(",");
	var retStr = str;
	
	if(cnStr.indexOf(",") != cnStr.lastIndexOf(","))
	{
		if(str.charAt(indexComma-1) != '\\' && str.charAt(indexComma-2) != '\\') 
		{
			retStr = str.substring(0, indexComma) + "\\"  + str.substring(indexComma);
		}
	}
	return retStr;
}

// Fix for SPR #SSMH65WR3Q - Customer PMRs: St Lukes char escape issues (St Lukes crit sit pmr - 19076,500,000)
// These functions override the ones in peopleawareness_service.js.
function PAaddtoSTList(group) 
{
	STLinksAddToContactList(escapeSlashComma(linkId), group);
}

	
function STLinkClicked(person, personLinkText, status, event)
{
	linkId = person;

	if (typeof STLinksCreateIM == "function")
		STLinksCreateIM(escapeSlashComma(linkId));
}


// Fix for SPR #JROE665R2K - New users can't add users to contact list through peoplefinder portlet
// These functions override the ones in peopleawareness_service.js.
function STLinksPrivateGroupsReceived(groups)
{
	openAddToSTListJSP(groups);
}
function STLinksPrivateGroupsFailed(reason)
{
	openAddToSTListJSP(null);
}
function openAddToSTListJSP(groups) 
{
	pa_stlinksPrivateGroups = new Array();
	if (groups != null && groups.length > 0)
	{
		pa_stlinksPrivateGroups = groups.split(";");
	}
	else
	{
		if (typeof pa_addtostlist_defaultGroup != "undefined")
			pa_stlinksPrivateGroups[0] = pa_addtostlist_defaultGroup;
		else
			pa_stlinksPrivateGroups[0] = "Work";
	}
	window.open(personMenuItem_addtostlisturl, "pa_addtostlist", "resizable=yes,width=400,height=500,toolbar=no,status=no,menubar=no", true);
}

