var ListGroup = {
	
	Lists			: [],
	
	//Set default values for certain options
	NoRecords		: "No Records", //text that appears in list when no options are available
	PleaseSelect	: "Please Select", //text for top option on required list boxes
	AnyOption		: "", //text for top option on non-required list boxes
	AnyValue		: "", //option value for top option on non-required list boxes

	AddList : function(page,name,childName,isTop,isRequired,popAllChild,selItems,AnyOption,AnyValue,NoRecords,PleaseSelect){
		AnyOption = (AnyOption) ? AnyOption : this.AnyOption;
		AnyValue = (AnyValue) ? AnyValue : this.AnyValue;
		NoRecords = (NoRecords) ? NoRecords : this.NoRecords;
		PleaseSelect = (PleaseSelect) ? PleaseSelect : this.PleaseSelect;

		this.Lists.push( new LinkedList(page,name,childName,isTop,isRequired,popAllChild,selItems,AnyOption,AnyValue,NoRecords,PleaseSelect) );
	}
	
}

function LinkedList(page,name,childName,isTop,isRequired,popAllChild,selItems,AnyOption,AnyValue,NoRecords,PleaseSelect){
		
	this.Page		= page;
	this.Name		= name;
	this.IsTop		= isTop;
	this.ChildName	= childName;	
	this.Required	= isRequired;
	this.SelItems	= selItems;
	this.AnyOption	= AnyOption;
	this.AnyValue	= AnyValue;
	this.NoRecords	= NoRecords;
	this.PleaseSelect=PleaseSelect;
	this.PopAllChild= popAllChild;
	this.ChangeCount= 0; //records no of times lists get populated. If popAllChild is on then we only do this on first showing.
	this.listbox	= getEl(name);
	if(this.listbox){
		addEvent(this.listbox,"change", function(){ ChkAny(this,AnyValue);selectField(name,page); });
	}else{
		addEvent(window,"load",function(){
			addEvent(getEl(name),"change", function(){
												ChkAny(this,AnyValue);
												selectField(name,page); 
			});												
		});
	}

	function ChkAny(el,AnyValue){
		if(el.options[0].value==AnyValue && el.options[0].selected==true){
			for(x=1;x<el.length;x++){
				el.options[x].selected=false;
			}	
		}
	}

	if(this.ChildName!=""){				
		var self		= this;
		this.IsParent	= true; //we know this list is related to another list and when a value changes here we need to repopulate the child list
		//if this item is the first list in the chain we need to create an onload event to populate all the lists and also create
		//all the necessary listeners to handle onChange events
		if(this.IsTop){
			if(this.listbox){
				setField(self.Name,self.SelItems); //very top list needs check to make sure any saved values are selected
				selectField(self.Name,self.Page,self.PopAllChild,self.Required);
			}else{
				addEvent(window,"load", function(){setField(self.Name,self.SelItems);selectField(self.Name,self.Page); });
			}			
		}
		//this is my own event that fires once this list has been populated to make subsequent lists populate correctly
		this.OnPopulate	= function(){selectField(self.Name,self.Page,self.PopAllChild,self.Required);}
	}else{
		this.IsParent = false;
	}
}

function setField(name,sel){
	if(Trim(sel)=="") return; //no need for anything as nothing to be selected
	var el = getEl(name);
	if(el&&el.options&&el.value==""){ //if options have already been set (server side) then ignore
		var x,i,arr=sel.split(",");
		for(x=0;x<el.options.length;x++){
			for(i=0;i<arr.length;i++){
				if(Trim(arr[i])==el.options[x].value){
					el.options[x].selected = true;
				}
			}
		}
	}
	return;
}

function selectField(name,page)
{
	if(Browser.AJAXEnabled==false){return;}
	//if this list has no child no need to collect values to pas to xmlHTTP to populate child with results
	var objList = getListObject(name,page,false);
	if(!objList){return;}
	var popAll=objList.PopAllChild;
	if(popAll && (objList.ChangeCount>0 || objList.SelItems.length>0)){popAll=false;}
	if(objList.IsParent){
		var childName = objList.ChildName;
		if(objList.ChildName=="") return;
	}	
	var catID="";
	var el = getEl(name);
	for(var x=0;x<el.options.length;x++){
		if((popAll) || el.options[x].selected){
			if(el.options[x].value!=""){catID+=el.options[x].value+",";} //add values if not blank
		}
	}
	if(catID==""||catID==","){catID=0;}
	else{ catID=catID.replace(/,$/,"").replace(/^,/,"");}
	objList.ChangeCount++;
	// JOE MOD
	//var qry = 'cat=' + enc(catID);
	var qry = 'value=' + enc(catID) + '&listname=' + childName + '&category=' + enc(document.getElementById('categories').value);
	xmlhttpPost('http://www.artworkersguild.org/index.php/members/members_query/',qry,'populateLinkedList',childName,page); 	
}

// function to return a reference to the linked list object either for an item or that items child
function getListObject(name,page,getChild)
{
	var objList;
	if(ListGroup.Lists&&ListGroup.Lists.length>0){
		for(var x=0;x<ListGroup.Lists.length;x++){
			if(ListGroup.Lists[x].Name==name && ListGroup.Lists[x].Page==page){
				if(getChild){
					return getListObject(ListGroup.Lists[x].ChildName,page,false);
				}else{
					return ListGroup.Lists[x];					
				}
			}
		}
	}
	return objList;
}


function populateLinkedList(strIn,name,page) { 
	var content = "";  
	var rowCount = 0; 
	var contentArray; 
	var addNo = 0; 
	var objList = getListObject(name,page,false); //get a reference to the object we need to populate by name	
	if(typeof objList!="object"){return;}
	// Split results from string into array of list items. Could replace with a JSON object but kept simple
	contentArray =strIn.split('\n'); 
	var elTarget=objList.Name;
	if(getEl(elTarget)){ 
		var elT = getEl(elTarget); 
		elT.options.length=0; 	//reset
		if(contentArray.length-1>0){ 
		
			
			if(objList.Required){ //if field is required then first option is Please Select
				// JOE MOD
				/*
				elT.options[0]=new Option(); 
				elT.options[0].value="";
				elT.options[0].text=objList.PleaseSelect;
				addNo = 1; 
				*/
			}else{ //if field is not required then first option is always "Any","All" or whatever option you want to use
				elT.options[0]=new Option(); 
				elT.options[0].value=objList.AnyValue;
				elT.options[0].text=objList.AnyOption;
				addNo = 1; 
			} 
						
			var sel = objList.SelItems;
			if(sel&&sel.length){
				if(typeof(sel)=="string") sel=sel.split(",") //make sure we have array of values to select
			}
			for (var i = 0; i < contentArray.length-1; i++) { 
				// Create new category object for each list item 
				objCategory = new categoryListing(contentArray[i]); 
				elT.options[i+addNo]=new Option(); 
				elT.options[i+addNo].value=objCategory.CategoryID; 
				elT.options[i+addNo].text=objCategory.Category;
				if(sel.length>=0){
					for(var s=0;s<sel.length;s++){
						if(objCategory.CategoryID==Trim(sel[s])){  
							elT.options[i+addNo].selected = true;
						}			
					}
				}
			} 
		}else{ //No options to display so we show the text set in the NoRecords property "No Records" or "No options" etc
			// JOE MOD
			/*
			elT.options[0]=new Option(); 
			elT.options[0].value = "";
			elT.options[0].text = objList.NoRecords;
			*/
			elT.options[0]=new Option(); 
			elT.options[0].value=objList.AnyValue;
			elT.options[0].text=objList.AnyOption;
			
		} 
		if(objList.IsParent){  //if list is parent of another list we have to populate it using the selected values from this list
			objList.OnPopulate();
		}
	} 
} 

function categoryListing(category) {
	var categoryArray = category.split('|');    
    this.CategoryID = categoryArray[0];
    this.Category   = categoryArray[1];	    
}


// *******************************************************************************************************************************************
// The following are helper functions that are referenced by the main linked list object code. They are the sort of functions that
// everyone will have their own version of or they may use a framework library such as JQuery, Prototype, YUI, Dojo etc. I have included them
// here so that the code is all contained within this one file.


function Trim(s){
	return s.replace(/^\s*/,"").replace(/\s*$/,"");
}

function addEvent( obj, type, fn, useCapture ) 
{
	var r=true;
	var useCapture = (useCapture) ? useCapture : false;
	
	// dont attach events to text or comment nodes
	if (obj && (obj.nodeType == 3 || obj.nodeType == 8) ) return false;
	
	// want to prevent dupe anon functions being attached which is still possible using addEventListener so as we dont add arrays to the DOM we
	// search the listener array for dupes
	if(obj.addEventListener){ //do this check first as Opera supports both attachEvent and addEventListener and also IE hopefully may support it in future
		// Add listener using DOM 2 method
		obj.addEventListener( type, fn, useCapture ); 		
	}else{ // Handle IEs event model and older DOM 0 event model
		var key = "ev__"+type;  // create key to identify this objects events array in DOM	
	
		if(obj[key]){
			// as we are attaching our array to the DOM against this object we can search this for dupes rather than the listener array
			if(inArray(obj[key],fn) > -1){
				return;
			}
		}else{ // new event type for this object

			obj[key] = [];

			// check for current DOM level 0 inline handler and if found add to array at start
			if(obj["on"+type]){
				obj[key][0] = obj["on"+type];
				kill(obj["on"+type]) //remove it
			}

			// as this is first time this event type has been attached to this object we point the RunIEEvent function accordingly
			if(obj.attachEvent){
				// attach the RunIEEvents function to this object for this event once only
				r = obj.attachEvent("on"+type,function(e){RunIEEvents(e,obj);}); //passing the object as a ref ensures
			}else{ 
				// revert back to DOM 0 support which will still support this keyword
				obj["on"+type] = RunIEEvents;
			}
		}
		// add new function to end position of array
		obj[key][obj[key].length] = fn;		
	}

	return r;
}


// function to handle 
function RunIEEvents(e,obj){	
	var rv=true,fe;
	e=StandardiseEvent(e,obj); //make sure we get correct event reference window.event/frame and set e.target,relatedtarget for event
	var key = "ev__"+e.type;  // create key to identify this objects events array in DOM
	
	// loop through all functions in our event array for this type and call them in order
	for(var i=0;i<obj[key].length;i++){
		if(typeof(obj[key][i])=="function"){			
			if(Function.call){
				fe = obj[key][i].call(obj, e )
			}else{
				obj.RunFunction = obj[key][i];
				fe = obj.RunFunction( e ) 
			}
			if(fe!="undefined") rv = fe && rv; //if func call returns true/false add to existing return value
		}	
	}
	if(obj.RunFunction) kill( obj.RunFunction );
	return rv;
}

// tries to delete a reference if not nullifies it
function kill(obj){
	if(obj){
		try{
			delete obj;
		}catch(e){
			try{
				obj = null;
				return true;
			}catch(e){
			}
		}
	}
	return false;
}

function removeEvent( obj, type, fn, useCapture ) {  
	var r = true;
	var useCapture = (useCapture) ? useCapture : false;

	// dont remove events to text or comment nodes as they wont have been added in first place
	if (obj && (obj.nodeType == 3 || obj.nodeType == 8) ) return false;

	if(obj.removeEventListener){
		obj.removeEventListener( type, fn, useCapture ); 
	}else{
		var key = "ev__"+type;
		if(obj[key]){
			var i =  inArray(obj[key],fn);
			if(i>-1){
				r = obj.detachEvent( 'on'+type, fn ); 		
				kill( obj[key][i] ); //remove DOM reference to events hash
			}
		}
	}
	return r;
}

function inArray(arr,v){
	for(var x=arr.length;x>=0;x--){ if(arr[x] && arr[x]===v) return x;}
	return -1;
}

// get correct reference to event object as this=window in IE however when in frames you need the correct window reference
function GetEvent(e,el){
	if(!el) el = this;
	e = e || (((el.ownerDocument || el.document || el).parentWindow || window).event);
	return e;
}

function SetEventTarget(e){
	if(!e.target) e.target = e.srcElement;
	switch (e.type) {
		case "mouseover":
			e.relatedTarget = e.fromElement;
			break;
        case "mouseout":
			e.relatedTarget = e.toElement;			
    }
	return e;
}

function StandardiseEvent(e,el){
	return SetEventTarget(GetEvent(e,el));
}

//Set up my cross brower function to reference elements by id. Used throughout site and as I don't want to keep checking for object
//support on each function call we do it once if possible. All new browsers support getElementById but this handles the 1% ancient browser crowd
if(_d.getElementById){ //standard
	getEl = function(id,doc){ if(!doc){ doc=_d; } return doc.getElementById(id);}
}else if(_d.all){ //handle pre IE 5
	getEl = function(id,doc){ if(!doc){ doc=_d; } return doc.all[id];}
}else if(_d.layers){ //dirty old NN but handle layers anyway
	getEl = function(id,doc){ 
		if(!doc) doc=_d;
		if( doc.layers[id] ) { 
			return doc.layers[id]; 
		}else {
			for( var x = 0, y; !y && x < doc.layers.length; x++ ) {
				y = getEl(id,doc.layers[x].document); 
			}
			return y; 						
		}
	}
}else{
	getEl = function(id,doc){ if(!doc){ doc=_d; } return doc[id];}
}


// AJAX Functions 
// These are pretty basic functions that are used to allow our linked lists to retrieve their options from the server without the page refreshing.

/*	All pages that use linked lists or other AJAX functionality should check this flag before linking lists and calling any futher ajax functions
	as if its not undefined it will let you know whether to both trying to create objects or not. */
function enc(val){
	if(typeof(encodeURIComponent)=="function"){
		return encodeURIComponent(val);
	}else{
		return escape(val);
	}
}
function getXmlHttp(){
	var xmlHttp;	
	var ProgID, ieProgId = "";
	var init = false;
	// ProgIDs for MS XMLHTTP object. We will loop until we get one.
	var XML_ACTIVE_X_IDENTS = [
		  "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0",
		  "MSXML2.XMLHTTP", "MICROSOFT.XMLHTTP.1.0", "MICROSOFT.XMLHTTP.1",
		  "MICROSOFT.XMLHTTP" ];
	// IE versions should support this object and we'd rather use it as its inbuilt with no "request for ActiveX object use msg to user"
	if(typeof XMLHttpRequest != 'undefined'){
		try{
			xmlHttp = new XMLHttpRequest();
			init = true;
		}catch(e){}
	}
	// However if we can't use that object and we have access to activeX (IE) then try this
	// IE browsers could use many different objects so we loop the array of prog IDs until we find one that works
	if (!init && typeof ActiveXObject != 'undefined') {
		for (var i = 0; i < XML_ACTIVE_X_IDENTS.length; i++) {
			ProgID = XML_ACTIVE_X_IDENTS[i];
			try {
				new ActiveXObject(ProgID);
				ieProgId = ProgID; //if here we got one				
				break;
			} catch (e) {
			// do nothing; try next choice
			}
		}
		if(ieProgId!=""){ //could be the last in the array and still not work
			try{				
				xmlHttp = new ActiveXObject(ieProgId);
				init = true;
			}catch(e){}
		}
	} 
	if (!init && window.createRequest) {
		try {
			xmlHttp = window.createRequest();
			init = true;
		} catch (e) {}
	}
	//set global flag in browser object so other JS functions know whether to even bother calling xmlHttpPost or other ajax functions
	if(init && typeof(xmlHttp) == "object"){
		Browser.AJAXEnabled = true; //set global flag to preven repeated calls to this function from xmlhttpost	
	}else{
		Browser.AJAXEnabled = false;
	}
	return xmlHttp;	
}

function xmlhttpPost(strURL, strSubmit, strResultFunc, param1, param2) {
	//A previous attempt will have set the Browser flag
	if(Browser.AJAXEnabled==false){ 
		return false;
	}else{
		//try and get a new local xmlhttp object
		var xmlhttp = getXmlHttp();
		if(typeof(xmlhttp) != "object" || !Browser.AJAXEnabled){
			return false;
		}
	}
	
	xmlhttp.open("POST",strURL,true);
	xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	// Create a new anonymous function that calls the handler function with saved parameters. arguments.callee is how you can reference the anonymous function from within itsself.
	var handler= function(){ handleXMLHttpResponse(arguments.callee.xmlhttp,arguments.callee.strResultFunc,arguments.callee.param1,arguments.callee.param2,arguments.callee.url,arguments.callee.submit) };
	/*	Now save the new xmlhttp as a property of the anonymous function.
		This allows you not to have to have a global XmlHttp object lying around,
		you can create them as needed and not interfere.	*/
	handler.xmlhttp=xmlhttp;
	handler.strResultFunc=strResultFunc;
	handler.param1=param1;
	handler.param2=param2;
	handler.url=strURL;
	handler.submit=strSubmit;
	xmlhttp.onreadystatechange=handler;				
	xmlhttp.send(strSubmit);			
}
function handleXMLHttpResponse(xmlhttp,strResultFunc,param1, param2, url, submit) {         
	if (xmlhttp.readyState == 4) {
		strResponse = xmlhttp.responseText;
		var statusCode = -1;
		try{
			statusCode = xmlhttp.status;	
			switch (xmlhttp.status) {								
			// Page-not-found error
				case 404:
						alert('Error: Not Found. The requested URL ' + 
								strURL + ' could not be found.');
					break;
			// Display results in a full window for server-side errors
			case 500:
				handleErrFullPage(strResponse);
				break;
			default:
				// Use a JS alert for custom error or debug messages
				if (strResponse.indexOf('Error:') > -1 || 
					strResponse.indexOf('Debug:') > -1) {
					alert(strResponse);
				}
				// Call the desired result function
				else {
					if(strResultFunc!=''){
						eval(strResultFunc + '(strResponse,param1,param2)');
					}
				}
				break;
			}
		}catch(e){return;}
	}
	return;
}

function handleErrFullPage(strIn) {
	//alert("handle full error = " + strIn)
	var errorWin;	
    // Create new window and display error
    try {
		errorWin = window.open('', 'errorWin');
		if(errorWin){
			errorWin.document.write('<html><head><title>Error Message</title></head><body>' + strIn + '</body></html>')
			errorWin.document.close();	
		}else{
			throw( strIn );
		}
    }
    // If pop-up gets blocked, inform user
    catch(e) {
            alert('An error occurred, but the error message cannot be' +
                  ' displayed because of your browser\'s pop-up blocker.\n' +
                  'Please allow pop-ups from this Web site.\n\n\The error message was\n\n' + strIn);
    }
}

