//BENINO NOTES
//**************************************************************************************************
//
//check for error response in save callback.  if error display alert and re-enable so user can try again 
//
//(tell bobby to add a product with no articles, for testing)
//
//***************************************************************************************************
		//GLOBALS
		//var currentURL = "getfieldsbyid.php";
		var currentURL = "/db/get_data.php";
			if (globalUser=='admin')
				currentURL = "/db/admin_get_data.php";
		var editURL = "/db/edit_data.php";
		var imageURL = "/db/image.php";
		var adminCommentURL = "/db/comment.php";
		var userCommentsURL = "/db/user_comment.php";
		var userCategoryURL = "/db/category.php";
		var searchURL = "/db/search.php";
		var ratingURL = "/db/rating.php";
		var globalSearchMode = false;  //is set to true if a search was performed.  
		//this is the globle array that will hold all articles once they are downloaded so that the server will not need to be contacted again.  
		//object is used instead of array because prototype.js extends the array object and breakd sthe for...in iterator for arrays.
		var articleArray = new Object();
		//will hold the pid of the currently selected product.  
		if (typeof(globalProductId) == "undefined")
			var globalProductId;
		//this array will hold an array of objects (aid and cid)  which will be sorted by cid for the user
		var globalArticleSort = new Array();
		//will hold the currently selected product in the sortDropDown Menu
		var globalSortSelected = "";
		//will be set to indicate when an edit is in progress
		var globalEditInProgress = false;
		
	
		function ajaxRequest(url,postBack,params,method)
		{
			//set transmission method if it was not included in the parameters
			if (!method)
				method = 'get';
			//add random number to parameters to prevent caching.
			var randomNumber = Math.floor(Math.random()*10000000);
			params.set('randomNumber', randomNumber);
			new Ajax.Request(url,
				  {
				    method:method, parameters:params,
				    onSuccess: postBack,
				    onFailure: function(){ alert('Something went wrong...') }
				  });
		}
		
		//*******************************************************************************************
		//this is the callback function for the request for all product ID's  and Product Names
		//the data is sent back in a json packet containing an array of products and product ID's 
		//for each product a cid list is sent back which indicates what categories are available for the given prodcut
		//the product data is  used to generate a list of products for the navication (admin user only), which are made into links
		//using the product ID in the link so that a click will load the headlines for all articles under that product ID
		//*******************************************************************************************
		function receiveProducts(products)
		{
			
			products = products.responseText.evalJSON();
			if(globalUser == "admin") 
			{//this is an admin user
				output = "";
				for (var x = 0; x<products.length; x++)	
				{	
					output += '<div id="nav' + products[x].pid + '" name="nav0" class="inactiveNavigation" onclick="getHeadlines(\''+ products[x].pid +'\')" onmouseover="this.className=\'activeNavigation\'" onmouseout="this.className=\'inactiveNavigation\'">'+ products[x].name +"</div>\n";
					//now if we don't already have this prodcut stored in memory  create an entry for it and add teh cid list to the entry
					if (articleArray[products[x].pid] == undefined)
					{
						articleArray[products[x].pid] = new Object();
						articleArray[products[x].pid].cid = products[x].cid;
					}
				}
				//write the product navigation links to the navigation div 
				$("navigation").innerHTML = "<br />" + output + "<br />";			
			}
			else  
			{//this is a public uaser
				for (var x = 0; x<products.length; x++)	
				{	
					if (products[x].pid == globalProductId)
					{
						//write the product name in the title bar
						$("titleBar").innerHTML = '<div id="productTitleLeft" ></div><div id="productTitle" ><a href="http://'+ products[x].name.replace(" ","") +'.com">' + products[x].name + '</a></div><div id="productTitleRight"></div>';
						break;
					}
				}
			}
		}
		
		
		//*******************************************************************************************
		//this is the callback function for the request for all categories for a specific product
		//the data is sent back in a json packet containing a variable with a comma separated list of category ID's
		//this list of category id's is the list of categories which are available for the current product.
		//this is used in conjunction with the list of all categorries and their correspondin ID's to know what to 
		//display in the navigation  area (for public users).
		//*******************************************************************************************
		function receiveProductCategories(productCategories)
		{
			//this function is only called by public users, so there are no need for any conditions in here about that
			var productCategories = productCategories.responseText.evalJSON();
			if (productCategories[0] === undefined)
			{//if the response array is length zero then no articles were returned
				//this should never happen.  There should never be a product with no categories.
				var output = "";
				output += "No Articles are available";
			}
			else
			{//headline data was returned in a JSON formatted packet
				var pid = globalProductId;
				if (articleArray[pid] === undefined)
				{	//create an entry in the article array for that product because it did not exits
					articleArray[pid] = new Object();
				}
				var tempCidArray = new Array();
				var addToArray;//will be used in inner loop, if duplicates found will be set to false to not add duplicate
				for(var x = 0; x < productCategories.length; x++)
				{
					var splitString=productCategories[x].cid.split(",");
					for(var y=0; y < splitString.length; y++)
					{
						addToArray = true;
						for (var z = 0; z < tempCidArray.length; z++)
						{
							if (tempCidArray[z] == splitString[y])
								addToArray = false
						}
						if (addToArray)
							tempCidArray[tempCidArray.length] = splitString[y];
					}
				}
				//now turn the tempCidArray into a comma separated string of CID's 
				articleArray[pid].cid = tempCidArray.join();
				tempCidArray[0] = 10;
			}
		}
		
		//*******************************************************************************************
		//this is the callback function for the request for all categories for all products. (the purpose of this list is to have for translation of pid to product name).
		//the data is sent back in a json packet containing an array of categories
		//a global categories list is assigned this data.
		//this is used to populate the multi-select box of categories when a user clicks to edit or add an article for admin.
		//and to pupulate the navigation for public users
		//*******************************************************************************************
		function receiveCategories(categories)
		{	
			//conditional statement.  if this function is called by a public user then the categories will be placed in the navigation area
			if (globalUser == "public")
			{
				//for a public user this list is used to to translate the list of category id's to the actual category name
				//it can't be used until we know the list of available categories for the current peoduct...  that's what this contidion is for:
				if (articleArray[globalProductId] == undefined)
				{
					setTimeout(this.receiveCategories.bind(this, categories), 100); 
				}
				else
				{	
					categories = categories.responseText.evalJSON();
					//categories is now an array of objects, each containing id  and name
					articleArray.categories = categories;
					//if we are in this "else" it means we have the category/cid list so we can call the function to translate it to a list of categories
					articleArray[globalProductId].categories = convertCidToName(articleArray[globalProductId].cid);
					//now sort the cid and categories
					sortCategories(articleArray[globalProductId])
					
					//create side by side arrays of CID's and categories so we can access both in the loop by the same index
					var tempCidArray = articleArray[globalProductId].cid.split(",");
					var tempCatArray = articleArray[globalProductId].categories.split(",");
					output = "";
					//this loop will create the navigation links for the categories available under the current product
					for (var x = 0; x < tempCidArray.length; x++)	
					{	
						//output += '<div id="nav' + articleArray.categories[x].id + '" name="nav0" class="inactiveNavigation" onclick="globalSortSelected=\''+ articleArray.categories[x].name +'\');getHeadlines(\''+ globalProductId +'\');">'+ articleArray.categories[x].name +"</div>\n";
						output += '<div id="nav' + tempCidArray[x] + '" name="nav0" class="inactiveNavigation" onclick="getHeadlines(\''+ trim(tempCatArray[x]) +'\')" onmouseover="this.className=\'activeNavigation\'" onmouseout="this.className=\'inactiveNavigation\'">'+ trim(tempCatArray[x]) +"</div>\n";
					}
					$("navigation").innerHTML = "<br />" + output + "<br />";
				}
				//we have received categories and populated the navigation area.  Now if this is a single article view
				//we need to get that article
				if(globalSingleView != 0)
				{
					getAllFieldsById(globalSingleView, 'aid', 'view');
				}
			}
			else
			{
				//this "else" is for admin users.  For admin this data is  needed for reference,  so it is stored in memory
				//it is used to translate cid/categories.  
				if (typeof(categories.request.parameters.manage) !== 'undefined')
					var manage = true;
				else
					var manage = false;
				categories = categories.responseText.evalJSON();
				//categories is now an array of objects, each containing id  and name
				articleArray.categories = categories;
				articleArray.categories.sort(sortByName);
				//the category data is available now for the "manage categories" control, display that if this was triggered by a click to manage Categories
				if (manage)
				{
					var output = "";
					//top rounded corners
					output += '<b class="rtop"><b class="r1"></b> <b class="r2"></b> <b class="r3"></b> <b class="r4"></b></b>';
					output += '<div id="manage" >';
					output += '<div id="manageCatList" class="manageContents" >';
					output += '<strong>Existing Categories:</strong><br />';
					for(var x = 0; x<articleArray.categories.length; x++)
					{
						output += articleArray.categories[x].name + '<br />';
					}
					output += '</div>';
					output += '<div id="addCategory" class="manageContents" >';
					output += '<a href="javascript:showAddCategory();">Add a Category</a>';
					output += '</div>';
					output += '</div>';
					//bottom rounded corners
					output += '<b class="rbottom"><b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b></b>';
					document.getElementById("innerContent").innerHTML = output;
				}
			}
		}
		
		function sortByName(a,b)
		{	
			return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0));
		}
		
		function showAddCategory()
		{
			document.getElementById("addCategory").innerHTML = '<input id="addCategoryText" type="text" size="30"><input type="button" value="submit" onclick="addNewCategory();"><br />';
		}
		
		
		function addNewCategory()
		{
			var userCategory = document.getElementById("addCategoryText").value.toUpperCase();
			
			if (trim(userCategory).length > 0)
			{
				//check to make sure that category doesn't already exist:
				var submitCategory = true;
				for(var x = 0; x<articleArray.categories.length; x++)
				{
					if (articleArray.categories[x].name == userCategory) 
						submitCategory = false;
				}
				
				if (submitCategory)
				{//the category doesn't alredy exist, so submit it
					//disable the comment text box so that no changes will be made during the save
					document.getElementById("addCategoryText").disabled = true;
					var postBack = receiveAddNewCategory;					
					//serialize the form for transmission
					var parameters = new Hash();
					parameters.set("action","add");
					parameters.set("category",userCategory);
					//send the new category to the server
					ajaxRequest(userCategoryURL, postBack, parameters, "post");				
				}
				else
					alert("That category already exists.");
			}
			else //the comment text box was blank
			{
				alert("Please enter a category in the text box.");
			}
		}
		
		function receiveAddNewCategory(response)
		{
			if (response.responseText == "1")
			{
				alert("Category successfully added");
				getCategories('manage');
			}
			else
			{
				alert("response.responseText");
				document.getElementById("addCategoryText").disabled = false;
			}
				
		}
		
		/*************************************************************************************/
		//this function sorts the  categories received in receiveCategories function
		//there are side by side comma separated strings of categories and cid.  They must be kept in order
		//this function sorts the categories and then generates a new coma separated string of CIDs that correspond to the newly sorted categories
		//the changes are made to the product variable parameter directly because in javascript object variables are 
		//automatically passed by reference
		/*************************************************************************************/
		function sortCategories(product)
		{
			//convertCategoryToCid
			var cidString = "";//will hold the sorted comma separated list of cid's
			var categoryString = "";
			var categoryArray = product.categories.split(",");
			for (var x = 0; x < categoryArray.length; x++)
			{
				categoryArray[x] = trim( categoryArray[x] );
			} 
			categoryArray.sort();
			for (var x = 0; x < categoryArray.length; x++)
			{
				cidString += convertCategoryToCid(categoryArray[x]);
				categoryString += categoryArray[x];
				if (x+1 < categoryArray.length)
				{
					cidString += ",";
					categoryString += ", ";
				}
			}
			product.cid = cidString;
			product.categories = categoryString;
		}
		
		
		function receiveHeadline(question)
		{   
			alert("this never get's called");
			question = question.responseText.evalJSON();
			$(question.aid).innerHTML = writeHeadlineRow(question);
		}
		
		//*******************************************************************************************
		//this is the callback function for the request for all question ID's and basic elements to populate entire right hand pane.
		//for admin user:
		//requested all aID's and basic fields given one pid in one request so server can send them back in one big JSON packet.   
		//for public user:
		//requeted all aid's and basic fields for a given pid and category.
		//*******************************************************************************************
		function receiveHeadlines(articles)
		{	//if we have not received the categories, we'll wait and call this function again
			//if the user is a public user this case is not an issue because the first thing acquired is the list of categories

			if (articleArray.categories == undefined)
			{
				setTimeout(this.receiveHeadlines.bind(this, articles), 100); 
			}
			else
			{//categories have been received so proceed
				//first translate the list of categories available for this product into a string of categories
				//this only needs to be done for admin users.  public user had this done when the category list was received. (which is done to pupulate the navigation on page load)
				if (globalUser == "admin")
					articleArray[globalProductId].categories = convertCidToName(articleArray[globalProductId].cid);
				
				//now a check to make sure we received a jason packet (which means we are getting articles, and not some error message)
				if (articles.responseText.substring(0,1) != "[")
				{
					//the search result contains output if no articles were found.
					if (globalSearchMode)
						var output = '<table align="center"><tr><td><br><br><br>'+articles.responseText+'</td></td></table>';
					else
						var output = "No areticles were found.";
					
					$("innerContent").innerHTML = output;
				}
				else
				{	//we have a json packet of article(s)
					
					//there is no callTimeCategory if we are in search mode
					if (globalSearchMode)
						var callTimeCategory = "";
					else if (globalUser == "public")//get the request time category parameter 
						var callTimeCategory = articles.request.parameters.cid;
					
					if (articles.request.parameters.search)
						var searchResults = true;
					else
						var searchResults = false;
					
					var articles = articles.responseText.evalJSON();
						
					if (articles[0] === undefined)
					{//if the response array is length zero then no articles were returned
						var pid = globalProductId;
						//we don't want to create the sort dropdown or the link to add a new article if this is a public user.
						var output = "";
						//first write the row which allows the admin user the option of creating  new article entry for a given product (not for public users)
						if(globalUser != "public")
							output = '<span id="add'+pid+'"><table width="100%"><tr><td align="left" >'+writeSortMenu(true)+'</td><td align="right"><a href="javascript:displayAddArticle(\''+pid+'\')">Add An Article to '+ $("nav"+pid).innerHTML +'</a></td></tr></table></span>';
						output += "No Articles were found.";
						$("innerContent").innerHTML = output;
					}
					else
					{//headline data was returned in a JSON formatted packet
						pid = articles[0].pid;
						if (articleArray[pid] === undefined)
						{	//create an entry in the article array for that product because it did not exits
							articleArray[pid] = new Object();
						}
						//we receive a list of articles when a category is clicked, so
						//delete the globalArticleSort array so as to clear out another sort array from when anothe product was sorted
						globalArticleSort.length = 0;
						var aid;  //will hold the aricle ID of the current article as the array of articles is looped through
						//we don't want to go into the following loop if we are in global search mode and we receive articles without a searc score
						//because if we do, it means they were received from a previous category click, and not from a search
						if(!(globalSearchMode && searchResults==false))
						{	//now we will loop through the articles we received and set the categories.   If this is not a public user we will also populate the article category sort array.
							for (var x = 0; x < articles.length; x++)
							{//loop through the json array of article bojects and assign then to the corresponding aid named object in the array
								aid = articles[x].aid;
								//server returns the array of CID when in admin mode or searchmode.   
								//for public user click (category click)  obviously category is the one they clicked on, so the server does not return that info.
								//here we also don't we put the adignment of the new artical to the global array so that if we are in search mode, we only do this if the results are from the search
								if(globalUser == "admin" || globalSearchMode )
								{
									articles[x].cid = convertCidToName(articles[x].cid);
									articleArray[pid][aid] = articles[x];
								}
								else
								{
									articles[x].cid = convertCidToName(callTimeCategory);
									articleArray[pid][aid] = articles[x];
								}
								//we don't need to maintain the globalArticleSort array  if  this is a public user
								if(globalUser != "public")
								{
									globalArticleSort[x] = new Object();
									globalArticleSort[x].aid = articles[x].aid;
									globalArticleSort[x].cid = articles[x].cid;
								}
							}
							//call the function that creates all the html for the headline rows to be displayed in the main innerContent area.  
							var output = writeAllHeadlineRows(pid);
							if (globalUser == "public" && convertCidToName(callTimeCategory) == globalSortSelected  || globalSearchMode)
							{
								$("innerContent").innerHTML = output;
							
							//if public  here is where you call requesting all data for all articles received, to display it all data initially
							//the second condition is to make sure this isn't coming in after a new category has already been picked
							// Note: globalSortSelected holds the currently clicked category when the user is "public"
								for(var key in articles)
								{	//loop only through the articles, this "if" ensures that we only look at articles
									if(articles[key].aid)
									{	//now we know this element is an article
										getAllFieldsById(articles[key].aid, "aid", "view");
									}
								}
							}
							else if(globalUser == "admin")  
							{//we don't need to check the calltime product, since we don't have to wait for all to load, so just verify that this is an admin user
								$("innerContent").innerHTML = output;
							}
						}//end of if that checks for condition where we are in search mode but received headlines without a search score
					}//end the else which verifies that we have articles in the JSONdata
				}//end else that ensures we have JSON formatted data
			}//end else that ensures we have received and stored the category data in memory
		}
	
		//*******************************************************************************************
		// this is the callback function that recieves all of available data for a given article ID
		// This is used when someone clicks to view or edit an article
		//*******************************************************************************************
		function receiveAllFields(article)
		{
			//when a request for all fields is sent, an action parameter is supplied
			//get the action to send to the output funcion (example: view, edit...)
			action = article.request.parameters.action;
			//convert the JSON string into an object and then call the output funciton 
			//to write it to that articles location in the main content area.
			article = article.responseText.evalJSON();
			//the article is sent as the first(and only) element of an array.
			article = article[0];
			//cid comes as an comma separated list of cid's.  convert ehm to article names
			article.cid = convertCidToName(article.cid);
			
			//here check if article exists and creat if not (this only applies if single article view, because in that case we haven't received all headlines yet)
			if (articleArray[article.pid][article.aid] == undefined)
				articleArray[article.pid][article.aid] = new Object();			
				
			//if we are in global search mode then we need to save the search score for this article, before overwriting the article object
			if (globalSearchMode && articleArray[article.pid][article.aid].score)
				var score = articleArray[article.pid][article.aid].score;

			//put it in our global store of articles. 
			articleArray[article.pid][article.aid] = article;
			//now restore the score if applicable
			if (globalSearchMode && score)
				articleArray[article.pid][article.aid].score = score;
			//call the appropriate function to write the article html data so it can be displayed in the main innercontent area
			if (globalUser == 'admin')
			{
				var output = writeAllFields(article, action);
			}
			else
			{	
				//if we are in Single View then we have not written the headline rows and this needs to be done with the data
				//for this one article we just received.  Then the full view can be written.
				if (globalSingleView != 0)
				{
					//for single article view we will assume the first category if the article has multiple categories
					globalSortSelected  = article.cid.split(",");
					globalSortSelected = globalSortSelected[0];
					$("innerContent").innerHTML = writeAllHeadlineRows(article.pid);
					globalSingleView = 0;
				}

					var output = writeAllPublicFields(article,action);
					//now we can unset the globalSingleView variable so that the navigation will be able to allow display of other articles

			}
			//display the output in the main innercontent area.
			$(article.aid + "data").innerHTML = output;
			//change the controll display  
			if (globalUser == "public" && containsCategory(article.cid, globalSortSelected) || globalSearchMode)
			{
				document.getElementById(article.aid + "actions").innerHTML = "<a href='javascript:processItem(\"close\",\"" + article.aid + "\")'>Hide <img border='0' src='/images/arr_up.gif'/></a>";//&#9660;
			}
			else if (globalUser == "admin")
			{
				switch(action)
				{ //view and cancel are the same.  both actions take you to "view" mode so both actions cascade into each other.
				case "view":
				case "cancel":
					output = "<a href='javascript:processItem(\"close\",\"" + article.aid + "\")'>Hide</a> | " +
					"<a href='javascript:processItem(\"edit\",\"" + article.aid + "\");'>Edit</a> | " +
					"<a href='javascript:processItem(\"del\",\"" + article.aid + "\")'>Del</a>";
					document.getElementById(article.aid + "actions").innerHTML = output;
				break;
				case "edit":
					output = "<a href='javascript:processItem(\"cancel\",\"" + article.aid + "\")'>Cancel</a> | " +
					"<a href='javascript:processItem(\"save\",\"" + article.aid + "\");'>Save</a> | " +
					"<a href='javascript:processItem(\"del\",\"" + article.aid + "\")'>Del</a>";
					document.getElementById(article.aid + "actions").innerHTML = output;
				break;
				}
			}
			
			
			
		}


		//*******************************************************************************************
		// this is the callback function that recieves the response from a request to delete an article
		// This is used when someone clicks to view or edit an article
		//*******************************************************************************************
		function receiveDeleteResponse(response)
		{
			var aid = response.request.parameters.aid
			if (response.responseText == 1)
			{	
				//delete the entry from our article array object.
				delete articleArray[globalProductId][aid];
				//now delete the entry from the global article sort.
				for (var x = 0; x < globalArticleSort.length; x++)
				{
					if (globalArticleSort[x].aid == aid)
					{	
						globalArticleSort.splice(x, 1);
					}
				}
				//check if the user was currently editing that article when they chose to dlete.  if so, unset globalEditInProgress
				if ( document.getElementById("editHeadline"+aid) != null)
					globalEditInProgress = false;
				//now write all the headline rows again.  the deleted article won't be displayed because it has been removed from memory.
				$("innerContent").innerHTML = writeAllHeadlineRows(globalProductId);
			}
		}


		//*******************************************************************************************
		// this is the callback function that recieves the response from the sever when a user clicked to save an article
		//  that was being edited.  
		//*******************************************************************************************
		function receiveSaveResponse(response)
		{//benino
		// on failiure maybe u should re-enable edit or add form so they can try again
		//In this function need a check for successful response, else display a an alert and re-enable the form so they can try again.
		//for now nothing happens if they try to save an article and an error comes back from the server.
			
			//if success response do all this stiff VVV
			var responseAid = response.responseText;
			//alert("saveResponse " + responseAid.length);
			
			if (responseAid.length = 32)
			{//a 32 char aid was sent back.  This means save was successful
				globalEditInProgress = false;
				document.getElementById("categorySort").disabled = false;
				if (response.request.parameters.pid)
				{	//this means that this is a save from adding a new article (not just an edit)
					//adding new row and rewriteing table of headlines.
					var pid = response.request.parameters.pid;
					//now add the new article to the global articleArray
					articleArray[pid][responseAid] = new Object();
					articleArray[pid][responseAid].aid = responseAid;
					articleArray[pid][responseAid].pid = pid;
					articleArray[pid][responseAid].headline = $("headline"+pid).value;
					//articleArray[pid][responseAid].question = $("question"+pid).value;
					//articleArray[pid][responseAid].answer = $("answer"+pid).value;
					articleArray[pid][responseAid].hit = "0";
					articleArray[pid][responseAid].avg_rating = "N/A";
					articleArray[pid][responseAid].num_rating = "0";
					var categories = document.getElementById("categories"+pid);
					articleArray[pid][responseAid].cid = convertSelectToCategory(categories);
					
					//now update the globalAarticleSort array with the new article
					var newIndex = globalArticleSort.length;
					globalArticleSort[newIndex] = new Object();
					globalArticleSort[newIndex].aid = responseAid;
					globalArticleSort[newIndex].cid = articleArray[pid][responseAid].cid;
									
					//now that we have the new item.  we want to rewrite the whole headline row table 
					$("innerContent").innerHTML = writeAllHeadlineRows(pid);
				}//end of the check for the pid parameter in the xhr request which tells us this was a new article, not just an edit
				else
				{// assume since it is success, and it is not a new article that this is a save response from edit article
					//first update the local object for the current article being saved
					articleArray[globalProductId][responseAid].headline = $("editHeadline" + responseAid).value;
					articleArray[globalProductId][responseAid].question = $("editQuestion" + responseAid).value;
					articleArray[globalProductId][responseAid].answer = $("editAnswer" + responseAid).value;
					//now udate the photo captions
					for (var x = 0; x < articleArray[globalProductId][responseAid].num_image; x++)
					{
						articleArray[globalProductId][responseAid].image[x].caption = document.getElementById("editCaption"+articleArray[globalProductId][responseAid].image[x].uid).value;
					}
					//clear out the old cid list.  it will be replaced by the new data from the category select box
					articleArray[globalProductId][responseAid].cid = "";
					articleArray[globalProductId][responseAid].last_edit_date = "Today";
					//call function to loop through the list of categories and update the local object so that we can display the updated categories. 
					var categories = document.getElementById("editCategories"+responseAid);
					articleArray[globalProductId][responseAid].cid = convertSelectToCategory(categories);
					//now update the globalArticleSort array with the new cid list
					//need to loop through the globalArticleSort array until we find the current article.  then update the cid list for that article
					for (var x = 0; x<globalArticleSort.length; x++)
					{
						if (globalArticleSort[x].aid == responseAid)
						{
							globalArticleSort[x].cid = articleArray[globalProductId][responseAid].cid;
							x = globalArticleSort.length; //exiting loop
						}
					}
					//the id of the div where that article is in the innercontent is the article's AID.
					//first rewrute the headline in case there were any changes there
					document.getElementById(responseAid).innerHTML = writeHeadlineRow(articleArray[globalProductId][responseAid]);
					//display the all field output in the data area for that article
					//we want to go back to "view" mode now that the article is saved so call the function to generate that output.
					var output = writeAllFields(articleArray[globalProductId][responseAid],"view");
					document.getElementById(responseAid + "data").innerHTML = output;
					output = "<a href='javascript:processItem(\"close\",\"" + responseAid + "\")'>Hide</a> | " +
					"<a href='javascript:processItem(\"edit\",\"" + responseAid + "\");'>Edit</a> | " +
					"<a href='javascript:processItem(\"del\",\"" + responseAid + "\")'>Del</a>";
					document.getElementById(responseAid + "actions").innerHTML = output;
					
				}
			}//end if that checks for 32 char aid response.
		}		



		//*******************************************************************************************
		// this function  creates the html output for a detailed view of an article when "vidw" is clicked 
		//  from the article headline.  
		// this function is only called for public users.  there is a separate function for this for admin users.
		//the article parameter is an arcicle object containing all of the data that is needed to display 
		//the detailed view.
		//*******************************************************************************************
		function writeAllPublicFields(article, action)
		{
			var aid = article.aid;
			//these are the options the user will have in "view" mode.  They will be changed if other options are needed in other cases
			optionsArray = new Array;
			optionsArray[0] = "Close";
			optionsArray[1] = "Edit";
			optionsArray[2] = "Del";

			var output = "";  //will contain the formatted output to display the article or editing form for the article.


				//convert custom image tag into real image tag in article.answer
				var tempAnswer = article.answer;
				var regEx;
				//this loop goes through the article answer and searches for the image code in the text and replaces it with the appropriate image tag.  
				for (var x = 0; x < article.num_image; x++)
				{
					regEx = new RegExp("%%IMAGE"+article.image[x].uid+"%%","g");
					tempAnswer = tempAnswer.replace(regEx,'<table class="photoWithCaption" ><tr><td class="photoWithCaption" width="'+article.image[x].width+'"><img src="'+imageURL+'?imageid='+article.image[x].uid+'" width="'+article.image[x].width+'" height="'+article.image[x].height+'" /><br /><strong>'+article.image[x].caption+'</strong></td></tr></table>');
				}
				//we will only display the question if it is different from the headline:
				if (article.question != article.headline)
					output += '<strong>Q: <span id="question'+aid+'">' + article.question + '</span></strong><br /><br />';
				output += '<strong>A:</strong> <span id="answer'+aid+'">' + nl2br(tempAnswer) + '</span><br /><br />'+
						'<strong>Article Category: </strong> <span id="category'+aid+'">' + article.cid + '</span><br />';
						
			//we are currently not deisplaying the average rating or the number of ratings
			//output += '<strong>Average Rating: </strong> <span id="avg_rating'+aid+'">' + article.avg_rating + '</span><br />' +
			//output += '<strong>Number of Ratings: </strong> <span id="num_rating'+aid+'">' + article.num_rating + '</span><br />';

			//now output the ratid widget
			output +='<div class="ratingWidget"><strong>Rate:&nbsp;</strong><br />';
			var starFileName = "uglystar_gray.gif";
			for (var x = 1; x<=5; x++)
			{
				if (article.avg_rating >= (x - .5))
					starFileName = "uglystar_color.gif";
				else
					starFileName = "uglystar_gray.gif";
				output +='<img id="'+x+'_'+aid+'" width="18" height="18" src="/images/'+starFileName+'" alt="'+x+'" onmouseover="ratingMouseOver(\''+x+'_'+aid+'\')" onmouseout="ratingMouseOut(\''+aid+'\',\''+article.avg_rating+'\')" onclick="rate(\''+aid+'\', \''+x+'\')">';
			}
			output +='</div>'; //end of rating widget div
			
			//now add the direct link url text box
			output +='<strong>Direct link to this article:</strong> <br />';
			output +='<input type="text" readonly="readonly" class="editInputs" id="directUrl'+aid+'" value="http://www.kbresource.com/index.php?id='+globalProductId+'&aid='+aid+'" onclick="this.select();"/><br />'
			
			//now add the ability for users to submit a comment 
			output += '<a href="#" onclick="createUserComment(\''+aid+'\'); return false;">[+ comment]</a><span id="userComment_'+aid+'"></span>';
			
			
			//no...the previously written data isn't being lost here.  output is used in the data being assigned to the output variable.  
			output = "<div class=\"viewEditAddContainer\"><table cellspacing=\"0\" cellpadding=\"2\" align=\"center\" border=\"0\" width=\"100%\" >	" +
			"<tr  align='left' >" +
			"<td colspan=\"4\">" + output + "</td></tr>" +
			"</table></div>";
			return output;
		}

		
		//*******************************************************************************************
		// This function is called when a public user clicks to add a comment
		// this creates a text area for entering the comment and a submit button
		// which will call the submitUserComment function
		//*******************************************************************************************
		function createUserComment(aid)
		{
			var commentSpan = document.getElementById("userComment_" + aid);
			if (document.getElementById('userCommentText'+aid) == undefined)
			{
				commentSpan.innerHTML = '<br /><textarea id="userCommentText'+aid+'" scrollbar="no" style="overflow: auto;margin:5px 0px 0px 0px;height:30px;width:500px;"></textarea>';
				commentSpan.innerHTML += '<br /><input type="button" onclick="submitUserComment(\''+aid+'\')"  value="Submit Comment">';
 			}
				
		}
		
		
		//*******************************************************************************************
		// This function is called when a public user clicks submit after writing a comment
		// First the function disables the comment text area so that it will not be edited after being submitted.
		//  it then sends the new comment to the server and sets the callback function to:
		//  receiveSubmituserComment().
		//*******************************************************************************************
		function submitUserComment(aid)
		{
			var userComment = document.getElementById("userCommentText" + aid).value;
			
			if (trim(userComment).length > 0)
			{
				//disable the comment text box so that no changes will be made during the save
				document.getElementById("userCommentText" + aid).disabled = true;
				var postBack = receiveSubmitUserComment;					
				//serialize the form for transmission
				var parameters = new Hash();
				parameters.set("action","add");
				parameters.set("user_comment",userComment);
				parameters.set("aid",aid);
				//send the new comment to the server
				ajaxRequest(userCommentsURL, postBack, parameters, "post");				
			}
			else //the comment text box was blank
			{
				alert("Please enter a comment.");
			}
		}

		//*******************************************************************************************
		// This function receives the response from the server after a user comment has been submitted.
		//  If a 1 is returned by the server it signifies a successful comment save.
		//  if not, an error message is displayed.
		//  Either way, the text area and submit button are replaced by a message.
		// on a successfully submitted omment an e-mail is sent to the administrator as a notification
		//*******************************************************************************************
		function receiveSubmitUserComment(response)
		{
			var aid = response.request.parameters.aid;
			var commentSpan = document.getElementById("userComment_" + aid);
			//alert(response.responseText);
			
			if ( !isNaN(response.responseText) )
			{
				commentSpan.innerHTML = '<br />Comment Submitted';
			}
			else
			{
				commentSpan.innerHTML = '<br />' + response.responseText + '<br /> Try Again';
			}
		}
		
		
		
		
		//*******************************************************************************************
		// This function is called when a public user clicks on the rating widget to rate an article
		// the rating is a number from 1 to 5.  This function sends the rating and the article ID  to 
		// the server.
		//*******************************************************************************************
		function rate(aid, rating)
		{
			//prototype.js takes the parameters in a hash table in name:value pairs.  
			var param = new Hash();
			param.set("rating",rating);
			param.set("aid",aid);
			var postBack = receiveRateResponse;
			ajaxRequest(ratingURL,postBack,param,"post");
		}

		//*******************************************************************************************
		// This function is the callback function for when a user rated an article with the rating widget. 
		// The server sends back "1" for a successful rating.  If a "1" is received we disable the rating widget
		// and set the rating widget to display the rating they made.
		// if something other than a 1 is returned the rating widget doesn't respond indicationg that the rating wasn't
		// accepded
		//*******************************************************************************************
		function receiveRateResponse(response)
		{
			var aid = response.request.parameters.aid;
			var rating = response.request.parameters.rating;
			//set the star they clicked on as highlighted
			if (response.responseText == "1")
			{
				ratingMouseOver(rating + "_" + aid);
				//now make the mouseover/mouseout not work
				for (x=1; x<=5;x++)
				{
					document.getElementById(x + "_" + aid).onmouseover = "";
					document.getElementById(x + "_" + aid).onmouseout = "";
				}
				//thank the user for the rating
				alert("Thank you for rating this article.");
			}
			else
			{	//server didn't allow a rating.  Warn user that they have already rated. 
				alert("You have already rated this article.");
			}
		}
		
		
		//*******************************************************************************************
		//receives the id of the current rating star hovered (format: oneDigitRatingNumber_aid)
		//when a star is hovered it must light up as well as all of the lower level stars.
		//a loop is used to light up all stars up to and including the hovered one. 
		//it then grays out any higher rated stars.
		//*******************************************************************************************
		function ratingMouseOver(ratingId)
		{
			currentStar = parseInt(ratingId.substring(0,1));
			aid = ratingId.substring(2);
			for (var x = 1; x<=5; x++)
			{
				if (x <= parseInt(currentStar))
					document.getElementById(x + "_" + aid).src = "/images/uglystar_color.gif";
				else
					document.getElementById(x + "_" + aid).src = "/images/uglystar_gray.gif";
			}
		}

		//*******************************************************************************************
		//receives the article id of the article who's rating widget is the current one.
		//when the mouse leaves the rating widget this function grays out all of the stars
		//*******************************************************************************************
		function ratingMouseOut(aid,averageRating)
		{
			var starFileName = "";
			for (var x = 1; x<=5; x++)
			{
				if (averageRating >= (x - .5))
					starFileName = "uglystar_color.gif";
				else
					starFileName = "uglystar_gray.gif";

				document.getElementById(x + "_" + aid).src = "/images/"+starFileName;
			}
		}
		
		
		
		//*******************************************************************************************
		//This function creates the html output to display an article in detailed view.  This function can be called
		//when a user clicks view from headline view, clicks to edit an article, or clicks cancel from an edit. 
		//it is also programatically called after a successful article edit->save.  
		//this function is only called for an admin user.
		//*******************************************************************************************		
		function writeAllFields(article, action)
		{
			var aid = article.aid;
			var output = "";  //will contain the formatted output to display the article or editing form for the article.
			switch(action)
			{ //view and cancel are the same.  both actions take you to "view" mode so both actions cascade into each other.
			case "view":
			case "cancel":

			//convert custom image tag into real image tag in article.answer
			var tempAnswer = article.answer;
			var regEx;
			//this loop goes through the article answer and searches for the image code in the text and replaces it with the appropriate image tag.  
			for (var x = 0; x < article.num_image; x++)
			{
				regEx = new RegExp("%%IMAGE"+article.image[x].uid+"%%","g");
				tempAnswer = tempAnswer.replace(regEx,'<table class="photoWithCaption" ><tr><td class="photoWithCaption" width="'+article.image[x].width+'"><img src="'+imageURL+'?imageid='+article.image[x].uid+'" width="'+article.image[x].width+'" height="'+article.image[x].height+'" /><br /><strong>'+article.image[x].caption+'</strong></td></tr></table>');
			}

			output = '<strong>H: <span id="headline'+aid+'">'+article.headline+'</span></strong><br /><br />' +
					'<strong>Q: <span id="question'+aid+'">' + article.question + '</span></strong><br /><br />' +
					'<strong>A:</strong> <span id="answer'+aid+'">' + nl2br(tempAnswer) + '</span><br /><br />'+
					'<strong>Article Category: </strong> <span id="category'+aid+'">' + article.cid + '</span><br />'+
					'<strong>Admin Comment: </strong><a href="#" onclick="createAdminComment(\''+aid+'\'); return false;">[+ comment]</a><br /><span id="adminComments'+aid+'">';
					if (article.a_comments)
					{
						for (var x = 0; x <  article.a_comments.length; x++)
						{
							output += '<span class="aComment" id="adminComment'+article.a_comments[x].uid+'">'+article.a_comments[x].comment_date+' <a href="#" onclick="editComment(\''+article.a_comments[x].uid+'\',\''+aid+'\',\'a\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+article.a_comments[x].uid+'\',\''+aid+'\',\'a\'); return false;">delete</a><br /> '+article.a_comments[x].comment+'<br /></span>';
						}
					}
			output +='</span>';//close the adminComments span
					
			break;
			case "edit":
				//edit mode displays the editable data in text boxes.  It also adds links to prerform more editing funcitons such as uploading pictures and adding comments...
				output = '<form action="#" name="edit' + article.aid + '" id="edit' + article.aid + '">' +
						'<strong>Headline:</strong><br /><input type="text" class="editInputs" name="headline" id="editHeadline'+aid+'" value="'+article.headline+'" /><br />' +
						'<strong>Question:</strong><br /><textarea class="editInputs" name="question" id="editQuestion'+aid+'" rows="3" >'+article.question+'</textarea><br />' +
						'<strong>Answer:</strong><br /><textarea class="editInputs" name="answer" id="editAnswer'+aid+'" rows="10">'+article.answer+'</textarea><br />' +
						'<strong>Admin Comment: </strong><a href="#" onclick="createAdminComment(\''+aid+'\'); return false;">[+ comment]</a><br /><span id="adminComments'+aid+'">';
				//here loop through the comments and create text boxes for each   (administrator comments)
				if (article.a_comments)
				{
					for (var x = 0; x <  article.a_comments.length; x++)
					{
						output += '<span class="aComment" id="adminComment'+article.a_comments[x].uid+'">'+article.a_comments[x].comment_date+' <a href="#" onclick="editComment(\''+article.a_comments[x].uid+'\',\''+aid+'\',\'a\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+article.a_comments[x].uid+'\',\''+aid+'\',\'a\'); return false;">delete</a><br /> '+article.a_comments[x].comment+'<br /></span>';
					}
				}
				output +='</span>';//close the adminComments span
				//now create the html for the category select box.
				output +='<strong>Categories:</strong><br /><select multiple size="5" name="category[]" id="editCategories'+article.aid+'">';
				var selected = "";//used to indicate which categories are currently selected
				//currentCategoryArray is the categorie(s) that is (are)  listed for this specific article
				var currentCategoryArray = article.cid.split(",");
				//var availableCategoryArray = articleArray[article.pid].categories.split(",");
				for (var x = 0; x < articleArray.categories.length; x++)
				{
					selected = "";
					for (var y = 0; y < currentCategoryArray.length; y++)
					{
						
						//the following code trimms wite space from beginning and end of string .replace(/^\s*|\s*$/g, "")   
						if ( currentCategoryArray[y].replace(/^\s*|\s*$/g, "") == articleArray.categories[x].name.replace(/^\s*|\s*$/g, ""))
						{//one of the trimmed category names in the array of categories for this product matched the current option in the select box so select it and exit hte loop
							selected = "selected";
							y = currentCategoryArray.length;//this exits the inner loop
						}
					}
					//for each option the value contains the cid and the text of the option contains the name of the category 
					output+='<option '+selected+' value="'+articleArray.categories[x].id+'">'+articleArray.categories[x].name+'</option>';
				}
				//close the category select box form element
				output +='</select><br />';
				output += '</form>';
			break;
			}

			//these fields are not editable so they get outputed the same wether it is view or edit (aside from the photo add link).			
			output += '<strong>Average Rating: </strong> <span id="avg_rating'+aid+'">' + article.avg_rating + '</span>';
			if (parseInt(article.num_rating) > 0)
				output += ' <a href="#" onclick="clearRatings(\''+aid+'\'); return false;">[Clear All Ratings]</a>';
			output += '<br /><strong>Number of Ratings: </strong> <span id="num_rating'+aid+'">' + article.num_rating + '</span><br />' +
//HIT COUNT NOT VALID					'<strong>Article Hit Count: </strong> <span id="hit'+aid+'">' + article.hit + '</span><br />' +
					'<strong>Number of User Comments: </strong> <span id="userCommentCount'+aid+'">'+article.num_comment+'</span>';
			if (article.u_comments)
			{
				output += ' <a href="#" id="toggleComments'+aid+'" onclick="toggleShowUserComments(\''+aid+'\'); return false;">Show Comments</a><br />';
				output += ' <div id="userComments'+aid+'" style="display: none;">';
				for (var x = 0; x <  article.u_comments.length; x++)
				{
					output += '<span class="uComment" id="userComment'+article.u_comments[x].uid+'">'+article.u_comments[x].comment_date+' <a href="#" onclick="editComment(\''+article.u_comments[x].uid+'\',\''+aid+'\',\'u\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+article.u_comments[x].uid+'\',\''+aid+'\',\'u\'); return false;">delete</a><br /> '+article.u_comments[x].comment+'<br /></span>';
				}
				output += '</div>';//end of userComments'aid' div
			}

			//now add the direct link url text box
			output +='<br /><strong>Direct link to this article:</strong><br />';
			output +='<input type="text" readonly="readonly" class="editInputs" id="directUrl'+aid+'" value="http://www.kbresource.com/index.php?id='+globalProductId+'&aid='+aid+'" onclick="this.select();" /><br />'
			
			output +='<br /><strong>Date Created: </strong> '+article.date_created+'<br />' +
					'<strong>Date Last Modified: </strong> '+article.last_edit_date+'<br />' +
					'<strong>Number of Pictures:</strong> <span id="num_image'+aid+'">' + article.num_image + '</span>';
			if (action == "edit")
				output +=' <a href="#" onclick="createUploadDialog(\''+aid+'\'); return false;">[+ photo]</a><br />';
			else
				output +='<br />';

			output += '<div id="photos'+aid+'">';
			//now if the number of images is > 0 then create the thumbnails
			if (article.num_image > 0)
				output += writePhotoTable(article, action);
			output += '</div>';  //close the "photos"+aid  div
			
			//build the complete output.  no... the output data isn't being lost by this assignment.  the output variable is included in what is being assigned here:
			output = "<div class=\"viewEditAddContainer\"><table cellspacing=\"0\" cellpadding=\"2\" align=\"center\" border=\"0\" width=\"100%\" >	" +
			"<tr  align='left' ><td colspan=\"4\">" + output + "</td></tr>" +
			"</table></div>";
			
			return output;
		}
		
		function toggleShowUserComments(aid)
		{
			if(document.getElementById('toggleComments'+aid).innerHTML == "Show Comments")
			{
				document.getElementById('toggleComments'+aid).innerHTML = "Hide Comments"
				document.getElementById('userComments'+aid).style.display = "block";
			}
			else
			{
				document.getElementById('toggleComments'+aid).innerHTML = "Show Comments"
				document.getElementById('userComments'+aid).style.display = "none";			
			}
			return false;
		}

		
		function clearRatings(aid)
		{
			var input_box = confirm("Warning.  You are about to permanently clear all ratings from the server.\nClick OK to perform this action now or click Cancel");
			if (input_box==true)
			{
				var postBack = receiveClearRatings;
				var param = new Hash();
				param.set('action','clearRating');
				param.set('aid',aid);
				ajaxRequest(editURL,postBack,param,"post");
			}
		}
		
		function receiveClearRatings(response)
		{	
			var aid = response.request.parameters.aid;
			if(response.responseText == "1")
			{
				document.getElementById("num_rating"+aid).innerHTML = "0";
				articleArray['globalProductId']['aid'].num_rating = "0";
				document.getElementById("avg_rating"+aid).innerHTML = "0";
				articleArray['globalProductId']['aid'].avg_rating = "0";
			}
			else
				alert(response.responseText);
		}
		
		//*******************************************************************************************
		// This funciton is called when a public user clicks the search button 
		// It clears out the articles in memory so that when the search results are returned, those are the only ones 
		// that are displayed in the main innercontent area. 
		// it also sets the globalSearch Mode so that it is known that a search is on 
		// which is used for output purposes and for the receiving function that receives the results. 
		//*******************************************************************************************		
		function performSearch()
		{
			if (trim(document.getElementById("searchTerms").value) != "")
			{
				$("innerContent").innerHTML = '<br><br><br><br><table width="100%"><tr><td align="center"><br />Searching<br /><img src="/images/progress.gif" /></td></tr></table>';

				//clear the article array to make sure to make sure we display only newly received search results
				for(var key in articleArray[globalProductId])
				{	//loop only through the articles, this "if" ensures that we skip the extra cid and category lists and only look at articles
					if(articleArray[globalProductId][key].aid)
						delete articleArray[globalProductId][key];
				}

				//set the gobal Search mode so that we knnow we are in search mode for the output function to know what to display
				globalSearchMode = true;
			
				var param = new Hash();
				param.set("search", trim(document.getElementById("searchTerms").value));
				param.set("pid",globalProductId);
				param.set('fields','aid|pid|headline|avg_rating|cid|score');
				var randomNumber = Math.floor(Math.random()*10000000);
				param.set('randomNumber', randomNumber);
				var postBack = receiveHeadlines;
				ajaxRequest(searchURL,postBack,param);
			}
			else
				alert("Please enter something in the search field.\nI can't search for \"nothing\"");
		}
		
		
		
		//*******************************************************************************************
		// This function is called when the admin user clicks to add a comment.  This function adds a text area to 
		// the current comment for the admin user to enter a new comment.  It adds a function to save the comment
		// onblur.
		//*******************************************************************************************
		function createAdminComment(aid)
		{//add a child element text area to "adminComments'+aid+'"
			//onBlur call the save function to save the comment
			if (document.getElementById('newAdminCommentSpan') == undefined)
			{
				var parentId = "adminComments"+aid;
				var parentNode = document.getElementById(parentId);
				var commentSpan = document.createElement("span");
				commentSpan.id = "newAdminCommentSpan";
				commentSpan.className = "aComment";
				commentSpan.innerHTML +='<textarea name="newAdminComment" id="newAdminComment" onBlur="saveComment(\'newAdminComment\',\''+aid+'\',\'a\')" scrollbar="no" style="overflow: auto;margin:5px 0px 0px 0px;height:30px;width:300px;"></textarea><br />';
				parentNode.appendChild(commentSpan);
				document.getElementById("newAdminComment").focus();
			}
		}

		//*******************************************************************************************
		// This function is called when the admin user clicks to add a comment.  This function adds a text area to 
		// the current comment for the admin user to enter a new comment.  It adds a function  call to save the comment
		// onblur.
		//*******************************************************************************************
		function editComment(uid, aid, type)
		{//adminComment+uid
			//depending on the type a = admin, u = user decide which array of comments we are referring to in this funciton
			var commentArray;
			if (type == 'a')
			{
				commentArray = articleArray[globalProductId][aid].a_comments;
			}
			else
			{
				commentArray = articleArray[globalProductId][aid].u_comments;
			}
		
			//get the comment that corresponds to the curren uid
			for (var x = 0; x < commentArray.length; x++)
			{
				if (commentArray[x].uid == uid)
				{
					var comment = commentArray[x].comment;
					//exit the loop because we found the comment in question 
					break;				
				}
			}
			//replace the innerHTML of the displayed comment with a text box containing the editable comment
			if (type == 'a')
			{
				document.getElementById("adminComment"+uid).innerHTML ='<textarea id="editAdminComment'+uid+'" onBlur="saveComment(\''+uid+'\',\''+aid+'\',\''+type+'\')" scrollbar="no" style="overflow: auto;margin:5px 0px 0px 0px;height:30px;width:300px;"></textarea><br />';
				document.getElementById("editAdminComment"+uid).value = comment;
				document.getElementById("editAdminComment"+uid).focus();//set the focus to the text box for editing
			}
			else
			{ //assuming type == 'u'
				document.getElementById("userComment"+uid).innerHTML ='<textarea id="editUserComment'+uid+'" onBlur="saveComment(\''+uid+'\',\''+aid+'\',\''+type+'\')" scrollbar="no" style="overflow: auto;margin:5px 0px 0px 0px;height:30px;width:300px;"></textarea><br />';
				document.getElementById("editUserComment"+uid).value = comment;
				document.getElementById("editUserComment"+uid).focus();//set the focus to the text box for editing
			}
		}
		
		//*******************************************************************************************
		//  This function sends a request to the server to delete an admin comment.  
		//  it sends the article ID which the comment belongs to and the UID of the comment to delete
		//*******************************************************************************************
		function deleteComment(uid,aid,type)
		{  
			var input_box=confirm("Warning.  You are about to permanently delete this Comment from the server.\n\nClick OK to continue or Cancel to keep the comment");
			if (input_box==true)
			{
				var action;
				if (type == 'a')
					action = "admin_delete";
				else
					action = "user_delete";
					
				var postBack = receiveDeleteComment;
				var param = new Hash();
				param.set('action',action);
				param.set('uid',uid);
				param.set('aid',aid);
				ajaxRequest(adminCommentURL,postBack,param,"post");
			}
		}

		//*******************************************************************************************
		//  this function receives  a response from the server after it has deleted a admin comment.
		//  if the comment was successfully deleted a comment count is returned.
		//  if a 1 is returned.  this deletes the comment node from teh dom tree.
		// and deletes the comment from the javascript memory
		//*******************************************************************************************
		function receiveDeleteComment(response)
		{
			if (!isNaN(response.responseText))
			{//successfully deleted.  Remove from html and from javascript object
				var uid = response.request.parameters.uid;
				var aid = response.request.parameters.aid;
				var action = response.request.parameters.action;
				
				if (action == 'admin_delete')
				{
					//delete the comment from the html
					var node = document.getElementById("adminComment"+uid);
					node.parentNode.removeChild(node);
					var commentArray = articleArray[globalProductId][aid].a_comments;
					var numComments = commentArray.length;
				}
				else
				{//assuming action == 'user_delete'
					//update the user comment count with the new count returned from the server
					document.getElementById('userCommentCount'+aid).innerHTML = response.responseText;
					//now delete the comment from the html
					var node = document.getElementById("userComment"+uid);
					node.parentNode.removeChild(node);
					var commentArray = articleArray[globalProductId][aid].u_comments
					var numComments = commentArray.length;
				}

				//now find the comment in the javascript object and delete it from memory
				for (var x = 0; x < numComments; x++)
				{
					if (commentArray[x].uid == uid)
					{//this is the comment we want to remove from the javascript object
						//delete this array element 
						commentArray.splice(x,1);
					}
				}//end loop that searches for the specifit admin comment uid and deletes it from the array
			}
		}
		
		//*******************************************************************************************
		//  This function is called to save an admin or user comment (this is done when the comment text area loses focus)
		//  the ID is either the UID of the existing user or admin comment or "newAdminComment"  if this is a newly created Admin comment.
		//  aid is the article id of the article which the comment belongs to.  type specifies 'a' if admin comment, 'u' if user comment.
		//*******************************************************************************************
		function saveComment(id, aid, type)
		{
			//the id of the span for the comment is either in the format editAdminComment_id (where id is the uid of the comment)
			// or for a comment that is just being created "newAdminComment"
			if (id != "newAdminComment")
			{
				if (type == 'a')
				{
					var spanId = "editAdminComment" + id;
					var action = "admin_edit";
				}
				else
				{//assuming type == 'u' for user comment
					var spanId = "editUserComment" + id;
					var action = "user_edit";				
				}
				
			}
			else
			{
				var spanId = id;
				var action = "admin_add";
			}

			var comment = document.getElementById(spanId).value;
		
			if (trim(comment).length > 0)
			{
				//disable the comment text box so that no changes will be made during the save
				document.getElementById(spanId).disabled = true;
				var postBack = receiveSaveComment;					
				//serialize the form for transmission
				var parameters = new Hash();
				parameters.set("action",action);
				parameters.set("aid",aid);
				//need to send the comment uid if this is an edit but not if it's a new comment
				if (action == "admin_edit" || action == "user_edit")
					parameters.set("uid",id);
				parameters.set("comment", comment);
				//send the new comment to the server
				ajaxRequest(adminCommentURL, postBack, parameters, "post");				
			}
			else //the comment text box was blank
			{//delete 
				if (action == "admin_add")
				{	
					//this comment is not on the server, it is new, so just delete the DOM node for it.
					var node = document.getElementById("newAdminCommentSpan");
					node.parentNode.removeChild(node);
				}
				else
				{//action was edit,  cuz we know it wasn't "add"
					//delete the comment from th server, since they deleted all the text.
					deleteComment(id, aid,type);
				}
			}
		}
		
		
		
		
		//*******************************************************************************************
		//  This function is the ajax callback function for when a comment has been saved
		//  The function removes the text area that was being used to edit/add the comment and then 
		//   replaces it with the edited/added comment.  
		//  The comment is added to the javascript object in memory.
		//*******************************************************************************************
		function receiveSaveComment(response)
		{
			//first make sure we have a numeric response from the server (numeric response would be the comment uid)
			if (!isNaN(response.responseText))
			{
				var aid = response.request.parameters.aid;
				var action = response.request.parameters.action;
				if (action == "admin_add")
				{ 
					var uid = response.responseText;
					var comment = document.getElementById("newAdminComment").value;
					//create the dom element for the newly added comment and add it to the comment adminComment area
					var parentId = "adminComments"+aid;
					var parentNode = document.getElementById(parentId);
					var commentSpan = document.createElement("span");
					commentSpan.id = "adminComment"+uid;
					commentSpan.className = "aComment";
					commentSpan.innerHTML='Today <a href="#" onclick="editComment(\''+uid+'\',\''+aid+'\',\'a\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+uid+'\',\''+aid+'\',\'a\'); return false;">delete</a><br /> '+comment+'<br />';
					parentNode.appendChild(commentSpan);
					//now delete the newComment text box
					var node = document.getElementById("newAdminCommentSpan");
					node.parentNode.removeChild(node);				
					
					//now add the comment to the javascript object in memory
					if (articleArray[globalProductId][aid].a_comments == undefined)
					{
						articleArray[globalProductId][aid].a_comments = new Array();
					}
					var x = articleArray[globalProductId][aid].a_comments.length;
					articleArray[globalProductId][aid].a_comments[x] = new Object();
					articleArray[globalProductId][aid].a_comments[x].uid = uid;
					articleArray[globalProductId][aid].a_comments[x].comment_date = "Today";
					articleArray[globalProductId][aid].a_comments[x].comment = comment;
				}
				else
				{// action == "admin_edit"  or "user_edit" because we know it wasn't "admin_add"
					var uid = response.request.parameters.uid;
					if (action == "admin_edit")
					{
						var comment = document.getElementById("editAdminComment"+uid).value;
						//replace the edit text box with the display of the newly edited comment.
						document.getElementById("adminComment"+uid).innerHTML='Today <a href="#" onclick="editComment(\''+uid+'\',\''+aid+'\',\'a\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+uid+'\',\''+aid+'\',\'a\'); return false;">delete</a><br /> '+comment+'<br />';
						//assign which comment array we are working with:
						var commentArray = articleArray[globalProductId][aid].a_comments;
					}
					else
					{
						var comment = document.getElementById("editUserComment"+uid).value;
						//replace the edit text box with the display of the newly edited comment.
						document.getElementById("userComment"+uid).innerHTML='Today <a href="#" onclick="editComment(\''+uid+'\',\''+aid+'\',\'u\'); return false;">edit</a> <a href="#" onclick="deleteComment(\''+uid+'\',\''+aid+'\',\'u\'); return false;">delete</a><br /> '+comment+'<br />';					
						//assign which comment array we are working with:
						var commentArray = articleArray[globalProductId][aid].u_comments;
					}
					//now add the comment to the javascript object in memory 
					
					for (var x = 0; x < commentArray.length; x++)
					{
						if (commentArray[x].uid == uid)
						{
							commentArray[x].uid = uid;
							commentArray[x].comment_date = "Today";
							commentArray[x].comment = comment;	
							//exit the loop because we found the comment in question 
							break;				
						}
					}
				}//end of "else"  that says we assume the action was "edit"
			}
			else
			{//did not receive a numeric response, so an error occured
				alert("Error Saving Comment");
			}
		}
		
		
		//*******************************************************************************************
		// this function requests the number of photos from the server.  if there are photos the server will also send back
		// an array of photo id's  (this function is called by the uploader once it has uploaded a file so that the photo 
		// area in the edit window can be updated.
		//*******************************************************************************************
		function getPhotos(aid)
		{
			//prototype.js takes the parameters in a hash table in name:value pairs.  
			var param = new Hash();
			param.set("aid",aid);
			param.set('fields','num_image');
			var postBack = receivePhotos;
			ajaxRequest(currentURL,postBack,param);
		}

		
		//*******************************************************************************************
		// This is the callback function for the request for the photos for a given article from the server
		// the response will contain jason encoded data.  a variable num_image will inidcat how many pictures there are
		// if there are images an array of images will be included, where each array element contains:
		//  uid, caption, width, height
		//*******************************************************************************************
		function receivePhotos(response)
		{//here we need to update the article object and then call the wirtePhototable function
		 //and update the photos+aid div innerHTML with that 
			aid = response.request.parameters.aid;
			//convert the JSON string into an object 
			photoInfo = response.responseText.evalJSON();
			photoInfo = photoInfo[0];
			articleArray[globalProductId][aid].num_image = photoInfo.num_image;
			document.getElementById("num_image"+aid).innerHTML = photoInfo.num_image;

			if (photoInfo.num_image > 0)
			{
				articleArray[globalProductId][aid].image = photoInfo.image;
			}

			//now get the photo output and replace the photo output area innerHTML with the new data
			var output = writePhotoTable(articleArray[globalProductId][aid], "edit");
			document.getElementById("photos"+aid).innerHTML = output;
			
		}
		
		//*******************************************************************************************
		// this function creates a table and populates it with the photo(s)  that correspond to the
		// article sent in the parameter.  
		//
		//the action parameter tells this function whether this is view or edit mode so that the function 
		//can display the edit and delete functionality as appropriate.
		//*******************************************************************************************		
		function writePhotoTable(article, action)
		{
			var imageInfo = article.image;
			output = '<table class="photo"><tr>';
			var tempPictureId = "0";//will hold the current picture ID as we loop through the array of picture ID's
			//this loop writes the first row of the table, which contains the images themselves
			for (x = 0; x<article.num_image; x++)
			{//output each thumbnail sized full res image
				tempPictureId = imageInfo[x].uid;
				output += '<th class="photo"><a href="'+imageURL+'?imageid='+tempPictureId+'" target="_blank" ><img class="thumbnail" src="'+imageURL+'?imageid='+tempPictureId+'" /></a> </th>';
			}
			output +='</tr>';
			
			//this loop  will write the captions
			output +='<tr>';
			for (x = 0; x<article.num_image; x++)
			{//output the Edit and delete Buttons
				tempPictureId = imageInfo[x].uid;;
				output += '<td class="photo">';
				if (action == "edit")
				{
					output+='Tag: %%IMAGE' + tempPictureId + '%%<br>';
					output+='<textarea class="captionEdit" name="editCaption'+tempPictureId+'" id="editCaption'+tempPictureId+'" rows="2">'+imageInfo[x].caption+'</textarea>';
				}
				else
				{
					output += imageInfo[x].caption;
				}
				output += '</td>';
			}
			output += '</tr>';

			if(action == "edit")
			{
				//this loop  will write the edit and delete commands
				output +='<tr>';
				for (x = 0; x<article.num_image; x++)
				{//output the Edit and delete Buttons
					tempPictureId = imageInfo[x].uid;;
					output += '<td class="photo"><a href="#" onclick="deletePhoto(\''+article.aid+'\',\''+tempPictureId+'\'); return false;">[delete]</a></td>';
				}
				output += '</tr>';
			}	
			output +='</table>';
					
			return output;
		}


		//*******************************************************************************************
		// this function is the ajax callback function for when a photo has been deleted from the server.
		// the response parameter will hold a 1 if the photo wa ssuccessfully deleted. 
		//
		// the delete request parameters are accessed to find out which photo was deleted.  
		//  the num_image information is updated in the article display
		//  the image data in javascript memory is looped through to remove the deleted photo.  
		//  the writephotoTable function is called to update the display of photos.
		//*******************************************************************************************		
		function receiveDeletePhotoResponse(response)
		{
			//update the article item to remove the photo ID, decrement photo count
			aid = response.request.parameters.aid;
			imageId = response.request.parameters.imageid;
			
			if (response.responseText = "1")
			{//photo successfully deleted
				//decrease photo count by 1
				articleArray[globalProductId][aid].num_image -= 1;
				document.getElementById("num_image"+aid).innerHTML = articleArray[globalProductId][aid].num_image;
				//now remove the imageId from the imageId array
				for (var x= 0; x < articleArray[globalProductId][aid].image.length; x++)
				{
					//alert("image ID being deleted: " + imageId + "\n\ncurrent image ID: " + articleArray[globalProductId][aid].image[x].uid);
					if (imageId == articleArray[globalProductId][aid].image[x].uid)
					{//delete this array element 
						articleArray[globalProductId][aid].image.splice(x,1);
						//exit the loop because we found and deleted the required image ID
						break;
					}
				}
				//now get the photo output and replace the photo output area innerHTML with the new data
				var output = writePhotoTable(articleArray[globalProductId][aid], "edit");
				document.getElementById("photos"+aid).innerHTML = output;
			}
			else
			{
				alert("error deleting photo");
			}
		}

		//*******************************************************************************************
		//  This function is called when an admin user clicks to delete a photo. 
		//  The user is warned that they are abbout to delete a photo from the server and is given the option to cancel.
		//*******************************************************************************************
		function deletePhoto(aid, imageID)
		{//server only needs the imageID sent to the delete function.
			
			var input_box=confirm("Warning.  You are about to permanently delete this photo from the server.\n\nClick OK to continue or Cancel to keep the image");
			if (input_box==true)
			{
				var postBack = receiveDeletePhotoResponse;
				var param = new Hash();
				param.set('action','delete');
				param.set('imageid',imageID);
				param.set('aid', aid);
				ajaxRequest(imageURL,postBack,param,"post");
			}
			return false;
		}

		//*******************************************************************************************
		//this request is to get all categories for all products
		//product name and product id
		// an extra request is triggered to get the cid's for the product in question if this is a public user
		//as this is needed to know which categories to display in the nav area.
		//*******************************************************************************************
		function getCategories(pid)
		{	
			//if this is a public user we need to request the category ID's  as well so we know 
			//which are the category id's that belong to the product in question
			//we also need to get the products so that we can display the product name that goes with the product ID
			if (globalUser=="public")
			{
				var param1 = new Hash();
				param1.set("pid",globalProductId);
				ajaxRequest(currentURL,receiveProductCategories,param1);
				//now get the product ID and names
				getProducts();
			}
			var postBack = receiveCategories;
			var param = new Hash();
			if (pid == 'manage')
			{
				//first check if an edit is in progress.  Give them the option of canceling or continueing
				if (globalEditInProgress)
				{//an edit or add is currently in progress.
					var input_box=confirm("Warning.  You are currently editing or adding a new article.\n\nClick OK to continue and lose the edit in progress or \nClick Cancel to remain here and continue editing");
					if (input_box==true)
					{//user chose to end that edit and lose changes
						globalEditInProgress = false;
					}
				}
				if (globalEditInProgress)
					return;
					
				param.set('manage', pid);
			}
			param.set('category', '1');
			//param.set('pid', pid);
			ajaxRequest(currentURL, postBack, param);
		}		
		
		
		//*******************************************************************************************
		// This function is called to request all of the products and corresponding product ID's.  
		// it is the first function called when the page is loaded as an admin user.  The data is used
		// to pupulate the nav area.  
		//*******************************************************************************************
		function getProducts()
		{	
			var postBack = receiveProducts;
			var param = new Hash();
			param.set('products', '1');
			ajaxRequest(currentURL, postBack, param);
		}

		//*******************************************************************************************
		//this function requests the basic fields to be displayed in the headline rows from the PHP script
		//it can request all fields for one specified quesiton ID, or it can request all fields for all questions
		//based on a product ID.  idType will either be pid(product id)  or aid (artielc id).
		//if the idType has a  comma separated CID  number this function will send that as a CID parameter so 
		//that the server will only return the filds requested from articles with the indicated CID.
		//*******************************************************************************************
		function getBasicFieldsById(id, idType)
		{
			//prototype.js takes the parameters in a hash table in name:value pairs.  
			var param = new Hash();
			if (globalUser == "public")
			{   //if the user is public a category can be appended to the idType 
				//here we get this cid and create another parameter from it.
				var tempArray = idType.split(",");
				if (tempArray.length > 1)
				{
					param.set('cid',tempArray[1]);
					idType = tempArray[0];
				}
			}
			param.set(idType,id);
			param.set('fields','aid|pid|headline|avg_rating|num_rating|hit|cid|num_image');
			if (idType == "pid")
			{	//here we are requesting headline row information for all articles under a given product ID
				var postBack = receiveHeadlines;
				ajaxRequest(currentURL,postBack,param);
			}
			else
			{	//only other choice is aid so idType is aid
				//here we will only be requesting one headline row's worth of information
				//this never happens. 
				var postBack = receiveHeadline;
				ajaxRequest(currentURL,postBack,param);
			}
		}

		//*******************************************************************************************
		//this function requests all possible fields to be displayed when view or edit are clicked in a headline row from the PHP script
		//it can request all fields for one specified quesiton ID, or it can request all fields for all questions
		//based on a product ID.  idType will either be pid(product id)  or aid (artielc id).
		//action is an extra parameter which will be sent to the server but not used.  will be accessed by the callback function to know
		//if the data returned is to be used to display the article information or to edit the article information.
		//*******************************************************************************************
		function getAllFieldsById(id, idType, action)
		{
			//prototype.js takes the parameters in a hash table in name:value pairs.  
			var param = new Hash();
			//will hold the fields to be requested from the server
			var fields = "";
			//an admin user needs more fields than a public user.  He we specify the correct fields to request based on the suer type.  
			if (globalUser == "admin")
				fields = 'aid|pid|question|answer|cid|headline|avg_rating|num_rating|hit|num_image|date_created|last_edit_date|num_comment|comments';
			else
				fields = 'aid|pid|question|answer|cid|headline|avg_rating|num_rating|num_image|num_comment|comments';
				
			param.set(idType,id);
			param.set('fields',fields);
			//this extra parameter will not be used on the server side but will be accessed by the callback function to know if the user wants to view or edit the data
			param.set('action',action);
			if (idType == "pid")
			{	//here we are requesting all information for all articles under a given product ID
				//this callback function should never be called with idtype==pid, because we shouldnt ever be calling getAllFieldByID for all articles under a product
				//for this reason receiveHeadlinse is left as the callback function to act as if a product had just been clicked (just in case)
				var postBack = receiveHeadlines;
				ajaxRequest(currentURL,postBack,param);
			}
			else
			{	//only other choice is aid so idType is aid
				//here we are requesting all fields for the given article ID
				var postBack = receiveAllFields;
				ajaxRequest(currentURL,postBack,param);  
			}
		}		
		
		//*******************************************************************************************
		//This function takes a product Id.  It will then create the html for the entire table of headline rows for that PID
		//using the data in the global articles array.  if this is a pulic user, only the currently selectedc category
		//stored in globalSortSelected will be displayed.
		//*******************************************************************************************
		function writeAllHeadlineRows(pid)
		{
			var output = "";
			var matchCount = 0;
			if (globalUser != "public")
			{
				//first write the row which allows the admin user the option of creating  new article entry for a given product if this is not a public user
				output = '<div  class="oddRow" id="add'+pid+'"><table width="100%"><tr><td align="left" >'+writeSortMenu(true)+'</td><td align="right"><a href="javascript:displayAddArticle(\''+pid+'\')">Add An Article to '+ $("nav"+pid).innerHTML +'</a></td></tr></table></div>';
				//time to sort the globalArticleSort array
				matchCount = sortArticles();
			}

			//this array is used to set every other row a different color for readability.  evenRow and oddRow are class names
			var tr_type = new Array();
			tr_type[0] = "evenRow";
			tr_type[1] = "oddRow";

			//this var will hold the current AID for each article as they are looped through in the following loop
			var aid;
			var counter = 0;
			
			var articleArrayCopy = new Array();
			//create a acopy of the array of articles we want to output
			//this gives us an array of articles instead of an object with articles as elements.  We also don't have th eextra elements such as cid and categories
			//this way the array can be sorted and navigated by numeric index
			var x = 0;
			for(var key in articleArray[pid])
			{	//loop only through the articles, this "if" ensures that we skip the extra cid and category lists and only look at articles
				if(articleArray[pid][key].aid)
				{
					articleArrayCopy[x] = new Object();
					articleArrayCopy[x] = articleArray[pid][key];
					x++;
				}
			}
			//debugger;
			//if we are in globalsearchmode we need to sort the articles according to their search score
			if(globalSearchMode)
			{
				articleArrayCopy.sort(sortByScore);
			}
			for(var key in articleArrayCopy)
			{	//loop only through the articles, this "if" ensures that we skip the extra junk added to the array object by prototype.js  (ugh prototype)
				if(articleArrayCopy[key].aid)
				{
					//allow this article to be displayed if we are not in single view OR if we are in single view and the article is the single one being viewed
					if(globalSingleView == 0 || articleArrayCopy[key].aid == globalSingleView)
					{
						//for a public user we will go through the articles in the order they were recieved.  
						//for an admin user we go through the articles in the order in which they are listed in the globalArticleSort Array
						if (globalUser == "public")
							aid = articleArrayCopy[key].aid;
						else
						{//we are in admin mode, so we are sorting based on the globalArticleSort array
							aid = globalArticleSort[counter].aid;
							//set the current element of the aritclearraycopy to the one we currently want to print
							articleArrayCopy[key] = articleArray[pid][aid];
						}
						
						//if the user is public we need to make sure we only display articles from the currently selected category.
						//if we are in search mode nowever, all extra articles have been removed and only search results are there, so we can go through them all.
						//benino (since articles can have more than one category u can't just compare cid to globalSortSelected directly, you need to see if globalsortselected is on of the categories held in cid
						if (globalUser == "admin" || (globalSearchMode && articleArrayCopy[key].score) ||(globalUser == "public" &&  containsCategory(articleArrayCopy[key].cid, globalSortSelected) && globalSearchMode == false))
						{
							tmp_rowType = tr_type[counter % 2];
							if (counter < matchCount)
								tmp_rowType = tmp_rowType + " highlight";
							output = output + "<div  class='" + tmp_rowType + "' >" +
							'<b class="rtop"><b class="r1"></b> <b class="r2"></b> <b class="r3"></b> <b class="r4"></b></b>' +
							'<div class="rowContent" id="' + aid + '">' +
							writeHeadlineRow(articleArrayCopy[key]) +
							'</div>' + 
							'<b class="rbottom"><b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b></b>' +
							"</div>\r\n";//end of row div
							counter++;
						}
					}
				}
			}
			//output += "</table>";
			return output;
		}
		
		//this function is needed to pass to the array.sort() function in order to sort the array of search sesults by score
		//this function is called from the writeAllHeadlineRows function to sort search results.
		function sortByScore(a,b)
		{
			if (a.score && b.score)
			{
				return b.score - a.score;
			}
			else
			{
				if (a.score)
					return "1" - "2";
				else if(b.score)
					return "2" - "1";
				else
					return "1" - "1";
			}	
		}
		
		
		//this function will sort the articles in the globalArticleSort array according to their first category
		//if a category is selected in sort Dropdown menu then articles containing that category go first
		function sortArticles()
		{
			//first deal with a possible selection from the category sort dropdown menu
			//the standard sort will start at the end of the matching sortBy elements which have been found and moved to the beginning of the array
			//this is zero by default:
			var matchCount = 0;
			if ($("categorySort") != undefined)
			{// the drop down menu exists, so check if anything is selected
				var sortBy = $("categorySort").value;
				if (sortBy != "")
				{
					var duplicateArticleSort = globalArticleSort.slice();//slice makes an independent copy of array instead of reference;
					var arrayCounter = 0;
					var categoryArray;//will be an array holding the list of categories
					//this first loop goes through the duplicate array and copies the matches to beginning of the globalArticleSort array
					for(var x=0; x < duplicateArticleSort.length; x++)
					{	//each article can have multiple categories.  so we need to split these up and check
						//if the caegory in question is there.
						categoryList = duplicateArticleSort[x].cid.split(",");
						for(var y = 0; y<categoryList.length; y++)
						{
							//the replace function just trims white space from beginning and end of string
							if (categoryList[y].replace(/^\s*|\s*$/g, "") == sortBy)
							{
								globalArticleSort[arrayCounter] = duplicateArticleSort[x];
								arrayCounter++;
								y = categoryList.length;  //this will exit the loop
							}
						}
					}
					//this second loop goes through and copies the non-mtches to the end of the globalArticleSortArray
					matchCount = arrayCounter;  //holds the number of matches found for the sortBy category
					var matchFound;  //boolean  value indicating if  a match has been found yet
					for(x=0; x < duplicateArticleSort.length; x++)
					{	//each article can have multiple categories.  so we need to split these up and check
						//if the caegory in question is there.
						categoryList = duplicateArticleSort[x].cid.split(",");
						matchFound = false;
						//alert("loop 2 x = "+x+"\n\ncurrent category: "+categoryList[0].replace(/^\s*|\s*$/g, ""));
						for(var y = 0; y<categoryList.length && matchFound == false; y++)
						{
							//the replace function just trims white space from beginning and end of string
							if (categoryList[y].replace(/^\s*|\s*$/g, "") == sortBy)
								matchFound = true;
						}
						if (matchFound == false)
						{//looped through the article's categories and none matched with the sortBy category
							//alert("mismatch @ array position: "+x);
							globalArticleSort[arrayCounter] = duplicateArticleSort[x];
							arrayCounter++;
						}
					}//end of loop that copies unmatching elements to the end of the globalArticleArray
				}
			}
			var tempArticle;
			//now sort the remainder of the articles lexicographically by first catery 
			for(var x=matchCount; x< globalArticleSort.length-1; x++)
			{
				
				for (var y=x+1; y<globalArticleSort.length; y++)
				{
					//alert("outerloop="+x+" Innerloop="+y+"\nglobalArticleSort[y].cid="+globalArticleSort[y].cid+"\nglobalArticleSort[x].cid="+globalArticleSort[x].cid+"\n\n(globalArticleSort[y].cid < globalArticleSort[x].cid)= "+ (globalArticleSort[y].cid < globalArticleSort[x].cid));
					if (globalArticleSort[y].cid < globalArticleSort[x].cid)
					{
						
						tempArticle = globalArticleSort[x];
						globalArticleSort[x] = globalArticleSort[y];
						globalArticleSort[y] = tempArticle;
					}
				}
			}
			return matchCount;
		}
		
		
		
		//*******************************************************************************************
		//This function takes an object containing the data that is needed to output the headline row
		//this function formats the output in a string and returns it so that the calling function can place it in the correct location
		//*******************************************************************************************
		function writeHeadlineRow(headlineRowJSON)
		{
			var output = "";
			
			output += "<table cellspacing='0' cellpadding='2' align='center' border='0' width='100%'>";
			if(globalSearchMode || globalUser=='admin')//we only want to display the headline in admin mode or if we are showing search results
				output += "<tr><td width='100%' align='left' colspan='2'><strong>(" + headlineRowJSON.cid + ")</strong></td></tr>";
			output += "<tr><td width='100%' align='left' colspan='2'><strong>" + headlineRowJSON.headline + "</strong></td></tr>" +		
			"<tr align='left'>";

			//the following conditional assignment ensures that we enly show the rating if the article has been rated more than once. 
			//var tempRating = headlineRowJSON.num_rating>1 ? "Average Rating: " + headlineRowJSON.avg_rating : "";			
			//for now we aren't displaying average rating
			var tempRating = "";
			
			if (globalUser=="admin")
				output +="<td width='100%' > Average Rating: " + headlineRowJSON.avg_rating + ", Num Ratings: " + headlineRowJSON.num_rating;// HIT COUNT NOT VALID + " , Hit Count: " + headlineRowJSON.hit + "</td>";
			else if (globalSearchMode)
				output +="<td width='100%' >" + tempRating + "&nbsp;&nbsp; Search Score: " + headlineRowJSON.score + "</td>";				
			else if (globalUser=="public")
			{
				output +="<td width='100%' >" + tempRating + "</td>";				
			}
			//var x = intTest == 1 ? 1 : 2 ;
			
			if (globalUser == "admin")
			{
				output += "<td nowrap><span id='"+headlineRowJSON.aid+"actions'><a href='javascript:processItem(\"view\",\"" + headlineRowJSON.aid + "\")'>Show</a> | " +
				"<a href='javascript:processItem(\"edit\",\"" + headlineRowJSON.aid + "\");'>Edit</a> | " +
				"<a href='javascript:processItem(\"del\",\"" + headlineRowJSON.aid + "\")'>Del</a></span></td>";
			}
			else
			{
				output += "<td nowrap><span id='"+headlineRowJSON.aid+"actions'><a href='javascript:processItem(\"view\",\"" + headlineRowJSON.aid + "\")'>Show <img border='0' src='/images/arr_down.gif'/></a></span></td>";
			}
			output += "</tr></table>"; 	
			output+= "<div id='"+headlineRowJSON.aid+"data' ></div>";

			return output;
		}
		
		function writeSortMenu(enabled)
		{  //if enabled is false then we want disable to be true
			var disabled = 'disabled="true"';
			var selected = ""; // used to select the correct option in the dropdown menu
			if (enabled)
				disabled = "";
			var output = '<form name="sortForm" id="sortForm">';
			output += 'Display This Category First:<select size="1" name="categorySort" id="categorySort" '+disabled+' onchange="sortChange()">';
			output+='<option value=""></option>';
			var categoryArray = articleArray[globalProductId].categories.split(",");
			//sort the categoryArray
			//categoryArray = 
			categoryArray[0] = " "+categoryArray[0];  //all the rest already have spaces in front so if I dont do this it won't sort correctly.
			categoryArray.sort();
			for (var x = 0; x < categoryArray.length; x++)
			{
				//trim
				categoryArray[x] = categoryArray[x].replace(/^\s*|\s*$/g, "");

				if(globalSortSelected == categoryArray[x])
					selected = "selected";
				else
					selected = "";
				output+='<option '+selected+' value="'+categoryArray[x]+'">'+categoryArray[x]+'</option>';
			}
			output +='</select><br /></form>';
			return output;
		}
		
		function sortChange()
		{//the user selected a different optino from the sort dropdown menu
			globalSortSelected = $("categorySort").value;
			$("innerContent").innerHTML = writeAllHeadlineRows(globalProductId);
		}
		
		//*******************************************************************************************
		//This function creates the form for the user to be able to add a new article item under the current Product
		//*******************************************************************************************
		function displayAddArticle(pid)
		{
			if (globalEditInProgress)
			{
				alert("You are currently editing an article.\n\nPlease cancel or save that edit to be able to perform this action");
			}
			else
			{
				globalEditInProgress = true;
				optionsArray = new Array;
				var output = '<form method="GET" name="addArticle" id="addArticle">' +
						'<strong>Headline:</strong><br /><input type="text" class="editInputs" name="headline" id="headline'+pid+'" value="" /><br />' +
						'<strong>Question:</strong><br /><textarea class="editInputs" name="question" id="question'+pid+'" rows="5" ></textarea><br />' +
						'<strong>Answer:</strong><br /><textarea class="editInputs" name="answer" id="answer'+pid+'" rows="10"></textarea><br />' +
						'<strong>Categories:</strong><br /><select multiple size="5" name="category[]" id="categories'+pid+'">';
						
				//here we used to calculate the availableCategoryArray as the categories currently used by the articles, but for adding a new areticle (or editing) all categories in the database should be available
				/*var availableCategoryArray = articleArray[pid].categories.split(",");
				availableCategoryArray[0] = " "+availableCategoryArray[0];
				availableCategoryArray.sort();*/
				
				var availableCategoryArray = articleArray.categories;
				
				for (var x = 0; x < availableCategoryArray.length; x++)
				{
					//if (articleArray.categories[x].pid == pid)
					output+='<option value="'+availableCategoryArray[x].id+'">'+trim(availableCategoryArray[x].name)+'</option>';
					
				}
				output +='</select><br /></form>';
				
				//output +=   here I need to add a way to upload pictures along with instructions on how to insert them into the Answer section
				//here the form is built for adding a new article.  the link is added to call addArticle when save or cancel are clicked
				output = "<div class=\"viewEditAddContainer\"><table cellspacing=\"0\" cellpadding=\"2\" align=\"center\" border=\"0\" width=\"100%\" >" +
				"<tr><td width=\"100%\ align=\"left\">"+writeSortMenu(false)+"</td>" +
				"<td><a href='javascript:addArticle(\"cancel\",\"" + pid + "\")'>Cancel</a></td>" +
				"<td><a href='javascript:addArticle(\"save\",\"" + pid + "\");'>Save</a></td>" +
				"</tr></table><table cellspacing=\"0\" cellpadding=\"2\" align=\"center\" border=\"0\" width=\"100%\" >	" +
				"<tr  align='left'>" +
				"<td colspan=\"4\">" + output + "</td></tr>" +
				"</table></div>";
				
				id = "add" + pid;
				$(id).innerHTML = output;
			}
		}
		//*******************************************************************************************
		//This is the function that is called when the user is done with the add article form 
		//it is either used to save or cancel the eidt.  Either it will send the new article to the server or it will discard it
		//but either way it will recreate the link allowing them to add another article.
		//*******************************************************************************************
		function addArticle(action, pid)
		{
			var cancel = false;
			switch (action)
			{
			case "save":
				// here ask if they sure they are done creating the article.  then if so send it to server, if not then continue editing
				var input_box=confirm("Warning.  You are about to save the article.\n\nClick OK to continue or Cancel to resume editing");
				if (input_box==true)
				{ // User clicked ok because they wish to save edited changes
					headlineId = "headline" + pid;
					questionId = "question" + pid;
					answerId = "answer" + pid;
					categoryId = "categories" + pid;
					//set callback function
					var postBack = receiveSaveResponse;					
					//serialize the form for transmission
					var article = new Hash();
					article.update($("addArticle").serialize(true));
					if (article.get("category[]") == ""  ||  article.get("headline") == ""  ||  article.get("question") == "" || article.get("answer") == "")
					{
						alert("please ensure that you have entered a Headline, Question, Answer, \nand that you have selected at least one category."); 
					}
					else
					{
						article.set("pid",pid);
						article.set("action", "add");
						//disable the form fields so no edits will be made while the article is being saved
						$(headlineId).disabled=true;
						$(questionId).disabled=true;
						$(answerId).disabled=true;
						$(categoryId).disabled=true;
						//send the new article to the server
						ajaxRequest(editURL, postBack, article, "post");
					}
				}
				//else do nothing so they can continue editing
			break;
			case "cancel":
				var input_box=confirm("Warning.  You are about to discard the article you are creating.\n\nClick OK to continue or Cancel to continue working on the article");
				if (input_box==true)
				{
					cancel = true;
				}
			break;
			}
			//now close the addArticle form and display just the link alloweing them to add another
			if (cancel)
			{
				var id = "add" + pid;
				var product = "nav" + pid;
				$(id).innerHTML='<table width="100%"><tr><td align="left" >'+writeSortMenu(true)+'</td><td align="right"><a href="javascript:displayAddArticle(\''+pid+'\')">Add An Article to '+ $(product).innerHTML +'</a></td></tr></table>';
				globalEditInProgress = false;
			}
		}
		
		//this function is used to perform an "ACTION":  view, edit, or delete, save.
		function processItem(action, aid)
		{
			if (action == "view" || action == "edit")
			{//view and edit are the same becuase the writeAllFields function handle the differences between the two 
			 //actions when displaying the data.  (either just display or put it in form text areas)
				if (action == "edit" && globalEditInProgress)
					alert("You are currently editing or adding an article.\n\nPlease cancel or save that edit to be able to perform this action");
				else
				{
					if (action == "edit")
					{
						globalEditInProgress = true;
						//disable the sort menu (can't sort while edit in progress)
						document.getElementById("categorySort").disabled = true;
					}
					getAllFieldsById(aid, "aid", action);
				}
			}
			else if (action == "close")
			{
				//and call the writeHeadlineRow  function with this  object as parameter to revert to headline mode for that row
				$(aid).innerHTML = writeHeadlineRow(articleArray[globalProductId][aid]);
		 
			}
			else if (action == "cancel")
			{	//user has clicked to cancel editing the article
				//here display a confirm box to allow them to resume editing if they don't really wish to cancel.  
				var input_box=confirm("Warning.  You are about to discard any changes you made while editing.\n\nClick OK to continue or Cancel to resume editing");
				if (input_box==true)
				{ 	// User clicked ok because they wish to cancel and discard edited changes
					//call following function to revert to previous data and return to view mode.
					var output = writeAllFields(articleArray[globalProductId][aid], "view");
					$(aid + "data").innerHTML = output;
					//update the action control links
					output = "<a href='javascript:processItem(\"close\",\"" + aid + "\")'>Hide</a> | " +
					"<a href='javascript:processItem(\"edit\",\"" + aid + "\");'>Edit</a> | " +
					"<a href='javascript:processItem(\"del\",\"" + aid + "\")'>Del</a>";
					document.getElementById(aid + "actions").innerHTML = output;
					//set the flag to indicate that an edit is no longer in progress;
					globalEditInProgress = false;
					//enable the category sort menu
					document.getElementById("categorySort").disabled = false;

				}
			}
			else if (action == "save")
			{	//user has clicked to save the article they were editing 
				//here display a confirm box to allow them to resume editing if they don't really wish to save.  
				var input_box=confirm("Warning.  You are about to replace the previous article content with the newly edited content.\n\nClick OK to continue or Cancel to resume editing");
				if (input_box==true)
				{ // User clicked ok because they wish to save edited changes
					//serialize the form for transmission
					var formVariables = new Hash();
					formVariables.update($("edit"+aid).serialize(true));
					//make sure they didnt leave any fields blank
					if (formVariables.get("category[]") == ""  ||  formVariables.get("headline") == ""  ||  formVariables.get("question") == "" || formVariables.get("answer") == "")
					{//one of the above fields was left blank during this edit
						alert("please ensure that you have entered a Headline, Question, Answer, \nand that you have selected at least one category."); 
					}
					else
					{//no fields were left blank
						//disable the form fields so that nothing can be edited while the form data is being saved
						$("editHeadline" + aid).disabled=true;
						$("editQuestion" + aid).disabled=true;
						$("editAnswer" + aid).disabled=true;
						
						document.getElementById("editCategories"+aid).disabled=true;
						//set callback function
						var postBack = receiveSaveResponse;					
						//create the parameter object to send to the server
						var param = new Hash();
						param.set('aid', aid);
						param.set('action', 'edit');
						if(articleArray[globalProductId][aid].num_image > 0)
						{
							var image = new Array();
							for(var x=0;x<articleArray[globalProductId][aid].num_image; x++)
							{
								//param.set('image[]',articleArray[globalProductId][aid].image[x].uid +"|"+ document.getElementById("editCaption"+articleArray[globalProductId][aid].image[x].uid).value);
								image[x] = articleArray[globalProductId][aid].image[x].uid +"|"+ document.getElementById("editCaption"+articleArray[globalProductId][aid].image[x].uid).value;
								document.getElementById("editCaption"+articleArray[globalProductId][aid].image[x].uid).disable=true;
								//captions[x].uid = articleArray[globalProductId][aid].image[x].uid;
								//captions[x].caption = document.getElementById("editCaption"+captions[x].uid).value
							}
							param.set('image[]',image);
						//param.set('captions', captions);
						}

						param.update(formVariables);
						//send the data to the server to be saved
						//alert(editURL);
						ajaxRequest(editURL, postBack, param, "post");
					}
				}
			}
			else if (action == "del")
			{	//user has clicked to delete an article from the database
				//if an edit is in progress only allow this action if the article they want to delete is the one they are currently editing
				if(globalEditInProgress && document.getElementById("editHeadline"+aid) == null)
				{
					alert("You are currently editing an article.\n\nPlease cancel or save that edit to be able to perform this action");
				}
				else
				{
					//here display a confirm box to allow them to cancel if they didnt mean to click delete.
					var input_box=confirm("Warning.  You are about to permenantly remove this article from the database.\n\nClick OK to continue or Cancel to keep the article.");
					if (input_box==true)
					{   // User clicked ok because they wish to delete the article
						//call following function to revert to previous data and return to view mode.
						var param = new Hash();
						param.set("action", "delete");
						param.set("aid", aid);
						var postBack = receiveDeleteResponse;
						ajaxRequest(editURL, postBack, param);
						//$(aid).innerHTML = "This article has been deleted";
					}
				}
			}
		}
		
		//this function takes a comma separated list of category id's and retuns a comma separated list of categories
		function convertCidToName(cidString)
		{
			//the split function is the same as php's explode.  Now cidArray will be an array of the cid's 
			if (cidString != "")
			{
				var cidArray = cidString.split(",");
				var productNames = "";  //will hold the comma separaged string of product names
				for (var x = 0; x< cidArray.length; x++)
				{
					for(var y = 0; y<articleArray.categories.length; y++)
					{
						if (articleArray.categories[y].id == cidArray[x])
						{	
							productNames += articleArray.categories[y].name + ", ";
							y = articleArray.categories.length;
						}
					}
				}
				//trim
				productNames = productNames.replace(/^\s*|\s*$/g, "");
				//remove trailing comma
				productNames = productNames.replace(/,$/,"");
				return productNames;
			}
			return "";
		}
		
		//this function takes a category name and retuns a category ID
		//*note:  unlike  convertCidToName this does only does one category at a time.  Not comma separated lists
		function convertCategoryToCid(category)
		{
			//the split function is the same as php's explode.  Now cidArray will be an array of the cid's 
			if (category != "")
			{
				for(var y = 0; y<articleArray.categories.length; y++)
				{
					if (articleArray.categories[y].name == category.replace(/^\s*|\s*$/g, ""))
					{	
						return articleArray.categories[y].id;
					}
				}
			}
		}	


		
		function convertSelectToCategory(categories)
		{//the categories parameter is a reference to the multi select box DOM element
			var currentNode = categories.firstChild;
			var productString = "";//will hold the string of selected products to return
			while ( currentNode != categories.lastChild )
			{	
				//make sure it's an options tag and not a <BR> tag or other...
				if (currentNode.nodeName.toUpperCase() == "OPTION")
					if (currentNode.selected)
						productString += currentNode.innerHTML + ", ";
				currentNode = currentNode.nextSibling;
			}
			//loop didn't iterate on the last child, so check the last node now
			if (currentNode.nodeName.toUpperCase() == "OPTION")
				if (currentNode.selected)
					productString += currentNode.innerHTML;
			//trim
			productString = productString.replace(/^\s*|\s*$/g, "");
			//remove trailing comma
			productString = productString.replace(/,$/,"");					
			return productString;
		}
		
		//receives a two parameters haystack and needle
		//haystack is a comma separated list of categories
		//returns true if needle is one of the categories in the comma separated list
		//returns false if not
		function containsCategory(haystack, needle)
		{
			if (trim(needle) == "" || trim(haystack) == "")
				return false;
				
			var explodedHaystack = haystack.split(",");
			for(var x = 0; x < explodedHaystack.length; x++)
			{
				if (trim(needle) == trim(explodedHaystack[x]))
					return true;
			}
			return false;
		}

	function nl2br(text)
	{
		text = escape(text);
		var re_nlchar = "";
		if(text.indexOf('%0D%0A') > -1)
		{
			re_nlchar = /%0D%0A/g ;
		}else if(text.indexOf('%0A') > -1)
		{
			re_nlchar = /%0A/g ;
		}else if(text.indexOf('%0D') > -1)
		{
			re_nlchar = /%0D/g ;
		}
		return unescape( text.replace(re_nlchar,'<br />') );
    }

	//trims white space from beginning and end of the input string and returns new string
	function trim(inputString)
	{
		return inputString.replace(/^\s*|\s*$/g, "");
	}
	
		
		//*******************************************************************************************
		//often an article object needs to be built from the data which is available through te DOM
		//this function builds and returns an object, given the article ID and action
		//the action is needed to know if to get the question answer headling from the form or not
		//*******************************************************************************************
		function buildArticleObject(action, aid)
		{	
			alert("DON'T CALL THIS FUNCTION");
			var article = new Object();
			article.aid = aid;
			if (action == "save")
			{	//get the headline question and answer fromt he form fields
				article.headline = $("editHeadline" + aid).value;
				article.question = $("editQuestion" + aid).value;
				article.answer = $("editAnswer" + aid).value;
			}
			else
			{	
				
				article.headline = $("headline" + aid).innerHTML
				article.question = $("question" + aid).innerHTML;
				article.answer = $("answer" + aid).innerHTML;
			}
			article.avg_rating = $("avg_rating" + aid).innerHTML;
			article.num_rating = $("num_rating" + aid).innerHTML;
			article.hit = $("hit" + aid).innerHTML;
			article.cid = $("category" + aid).innerHTML;
			article.num_image = $("num_image" + aid).innerHTML;
			
			return article;
		}
		
		
		//this function will request all categories and headlines
		//if the user is admin the product ID is the id of the current product.  
		//if this is a public user productID is the category they clicked (ascii human readable category, not CID);
		function getHeadlines(productId)
		{
			if (globalEditInProgress)
			{//an edit or add is currently in progress.
				var input_box=confirm("Warning.  You are currently editing or adding a new article.\n\nClick OK to continue and lose the edit in progress or \nClick Cancel to remain here and continue editing");
				if (input_box==true)
				{//user chose to end that edit and lose changes
					globalEditInProgress = false;
				}
			}		
			if( globalEditInProgress == false)
			{
				if (globalUser == "public")
				{
					//set the currently selected category;
					globalSortSelected = productId;
					//this was called because a category was clicked, so unset the globalSearchMode variable
					globalSearchMode = false;
					//convert the category name to a category ID so ti can be used to bold the correct 
					//category navigation label in the navigation area.
					productId = convertCategoryToCid(productId);
					//request all the data I need to populate the list of headline rows
					//here we add the caterogy ID to the PID parameter (comma separated).
					getBasicFieldsById(globalProductId, "pid,"+productId);
				}
				else
				{	//set the current globalProductId
					
					globalProductId = productId;
					//reset the globalSortSelected o blank since they are changing products
					globalSortSelected = "";
					if (document.getElementById("categorySort") != null)
						$("categorySort").selectedIndex = 0;

					//request the categories that are available under this product
					getCategories(productId);				
					//request all the data I need to populate the list of headline rows
					getBasicFieldsById(globalProductId, "pid");
				}
				

				//loop through all navigation div and unbold them all 
				var parentNode = document.getElementById("navigation");
				var currentNode = parentNode.firstChild;
				//there is no need to  go through the unbolding one last time after the loop because the last child is a <br> tag (not the last product div)
				while ( currentNode != parentNode.lastChild )
				{	
					//note (firefox itterates this loop for the br tabs the div tags and for something called #text which appears after
					//each div tag, so it must be the contend data (innerHTML)  of each div tag.  (not completely clear).
					if (currentNode.nodeName.toUpperCase() == "DIV")
					{
						currentNode.className="inactiveNavigation";
						//alert(currentNode.id);
						//document.getElementById(currentNode.id).onmouseout="this.className='inactiveNavigation'";
						currentNode.onmouseout = function()
							{
								this.className = "inactiveNavigation";
							}
						//currentNode.onmouseout="this.className='inactiveNavigation'";
						//currentNode.style.fontWeight="normal";
						//currentNode.style.fontStyle="normal";
						//currentNode.style.background="#555";
					}
					currentNode = currentNode.nextSibling;
				}

				//now bold the nav element that was clicked on
				// NOTE that this works for admin or public because product ID is a product ID or a category id as appropriate.
				var currentId = "nav" + productId;
				document.getElementById(currentId).className = "activeNavigation"
				document.getElementById(currentId).onmouseout= function()
					{
						this.className = "activeNavigation";
					}
				//"document.getElementById('"+document.getElementById(currentId).id+"').className='titlebar'";
			}
		}

		


