/*
	Objects to support client side javascript
	active scripting
	dcowden 4/13/2004
	
	Note that some methods have very short names because
	this code is included in downloads.
*/


/* **** ARRAY EXTENSION FOR NON-SUPPORTING BROWSERS **** */
if(typeof Array.prototype.push=='undefined') {
    Array.prototype.push = function () {
        var i=0,
            b=this.length,
            a=arguments;
        for(i;i<a.length;i++) {
            this[b+i]=a[i];
		}
        return this.length
    }
}

Array.prototype.contains = function ( item ){
	for ( var i=0;i<this.length;i++ ){
		if ( this[i] == item ) return true;	
	}
	return false;
}

//utility object not meant for instantiation
function BrowserUtils(){;}

//other typical options are [ 'mozilla','konqueror', 'safari','omniweb','opera','webtv','icab','compatible = Netscape Navigator',
BrowserUtils.SUPPORTEDBROWSERS = [ 'msie', 'mozilla' ];

//function to add an option to a select collection.
BrowserUtils.addSelectOption = function ( select, option ){
	if ( BrowserUtils.isIE() ){
		select.add(option);	
	}
	else{
		select.add(option,null);
	}	
}

BrowserUtils.isSupportedBrowser = function(){
	var detect = navigator.userAgent.toLowerCase();
	var OS,browser,version,total,thestring;
	
	for ( var i=0;i<BrowserUtils.SUPPORTEDBROWSERS.length;i++){
		var b = BrowserUtils.SUPPORTEDBROWSERS[i];
		if ( detect.indexOf(b) > -1 ){
			return true;
		}
	}
	return false;	
}

BrowserUtils.isIE = function (){	
	return navigator.userAgent.toLowerCase().indexOf("msie") > -1 ;	
}

function Logger(n){;}
Logger.getLogger=function(m){return new Logger(m)};
Logger.prototype.debug=function(m){;};
Logger.prototype.info=function(m){;};
Logger.prototype.warn=function(m){;};	
Logger.prototype.error=function(m){;};
Logger.setLevel=function(m,n){;};

/**
 * Map objec that provides storage of key-value pairs
 * @constructor
 */
function Map(){
	this.values = new Object();
	this.keys = new Array();	
	//this.log = Logger.getLogger("Map");	
}


/**
 * store the provided value under the provided key
 * @param key the key to store the value in
 * @param value, the object to store under the key
 */
Map.prototype.put = function(key,value){
	//this.log.debug("Putting " + key + "=" + value );
	
	if ( ! this.containsKey(key) ){
		this.keys.push(key);	
	}
	this.values[key] = value;
}

/**
 * retrieve the value under the provided key.
 * @returns the object found, or null if the key does not exist
 */
Map.prototype.get = function(key){
	return this.values[key];	
}

/**
 * Clear the values from the map
 */
Map.prototype.clear = function(){
	this.values = new Object();
	this.keys = new Array();	
}

/**
 * @returns an array that contains the keys in the map
 */
Map.prototype.listKeys = function(){
	return this.keys;	
}

/**
 * @returns an array containing all of the values in the map.
 * The array returned is in the same order as the values returned in listKeys()
  */
Map.prototype.listValues = function(){
	var vals = new Array();
	for ( var i=0;i<this.keys.length;i++){
		vals.push(this.values[this.keys[i]]);	
	}	
	return vals;
}

/**
 * @returns true if the map contains the specified key, false otherwise
 *
 */
Map.prototype.containsKey = function(key){
	var val = this.values[key];
	return (val != null);
}

/**
 * @returns true if the map contains the specified value, false otherwise
 *
 */
Map.prototype.containsValue = function(value){
	return (this.values[key] != null);
}

/**
 * @returns a string representation of the map in key=value,key=value format.
 */
Map.prototype.toString = function(){
	var result = "";
	for ( var i=0;i<this.keys.length;i++ ){
		result = result + this.keys[i] + "=" + this.values[this.keys[i]] + "\n";
	}	
	return result;
}

/**
 * Class represents a page. Each javascript page will only have one page object
 * that represents it. 
 
 * A page serves as a container for sections, and well as common functions
 * for page operations such as finding sections and members, etc.
	A page's primary responsibility is to be a section container, providing
	ability to find, and manage sections. it also provides a known, global
	entry point for page handlers that do not have references to javascript
	variables.
 * <pre>
 * Pages are at the top of the javascript object hierarchy.  The structure
 * looks like this:
          Page
          	--> 	Section*
          					-->  SectionMember*
          					-->  StateSelectComboBox
          					-->  SelectComboBox	
          					
    </pre>
	 @constructor
 */
function Page(){
	this.sections = new Map();
	//this.log = Logger.getLogger("Page");
	this.properties = new Map();
	this.focusId = null;
	this.scrollId = null;
	this.formName = "";
	this.objectPrefix = "jsPage";
}

/**
 * @return the one and only page object for the current javascript scope
 */
Page.currentPage = new Page();

/**
 * Initialize all of the sections, and the page itself.
 * The method is itended to be called from body.onload, and is designed
 * to allow all child sections and members to initialize, typically
 * executing script to bring the page to the desired initial state.
 */
Page.prototype.init = function(){
	var memberlist = this.sections.listValues();
	for ( var i=0;i<memberlist.length;i++){
		var sect = memberlist[i];
		sect.init();	
	}
	
	//focus on provide control, if there is one
	if ( this.focusId != null && this.focusId != ""){
		var item = document.getElementById(this.focusId);
		if ( item != null ){
			//this.log.warn("Focusing Element" + this.focusId );
			item.focus();	
		}	
	}
	
	//scroll view to  provided control, if there is one
	if ( this.scrollId != null && this.scrollId != ""){
		var item = document.getElementById(this.scrollId);
		if ( item != null ){
			//this.log.warn("Scrolling Element into view" + this.scrollId );
			item.scrollIntoView(true);	
		}	
	}	
}

/** 
* Updates the form specified by formName with a hidden element
* that represents the value specified
* used by sections to send their state to the server on a submit
* if formname is not specified, then the first form is used.
*/
Page.prototype.setFormValue = function( name, value ) {

	//disable if browser doesnt support well enough
	if ( ! BrowserUtils.isSupportedBrowser() ){
		return;
	}
	
	var fieldname = this.objectPrefix + "." + name
	if ( this.formName != null ){
		var form = document.forms[this.formName];
		//if not found, use the first form
		if ( form == null ){
			form = document.forms[0];
		}
		if ( form != null ){
			if ( form.elements != null ){
				var itm = form.elements[fieldname];
				if ( itm == null ){
					itm = document.createElement("input");
					itm.type = 'hidden';
					itm.name = fieldname;
					itm.value = value;
					form.appendChild(itm);
				}
				else{
					itm.value = value;
				}				
			}
			else{
				//this.log.warn("Form " + this.formName + " does not have any elements!");
			}
		}
		else{
			//this.log.warn("No Form Specified. Section state will not be preserved.");
		}
	}
}


/**
 * Add a section to the page.
 * @param newSection, a section object. See sections for more details, but
 * basically a page expects a section to have the following attributes:

      a name property
      an init() method					called when the page is initialized
      a setState( newState )  method called when a request to change state is made
      a containsId ( id )     method called when an event attempts to locate a section by using a document element
      
   Most typical uses of the javascript objects begin with an invokation of this method
   in order to find a section. for example, 
   <pre>
   	page.getSection(this).setState('hide')
   </pre>	   
   @returns the newly added section
 */
Page.prototype.aS = function(newSection){
	this.sections.put(newSection.name,newSection);
	return newSection;
}

/**
 * finds a section in the page, provided input object.
 * The method attempts a number of searches to find the section
 * for the object:
 
     (1)if the object has an id, grab the id, if not use the objects toString
     (2)determine if the string ( or object id) matches the standard section
        syntax ( section.state.#). if so, use the section name found there to search.
     (3)search for any section containing a member with the supplied id
     
   @param an input object or id to use for the search
   @returns the found section, or null if a section cannot be found.
 */
Page.prototype.getSection = function( obj ){
	var queryVal = null;
	if ( obj != null && obj.id != null ){
		queryVal = obj.id;
	}
	else{
		queryVal = obj;
	}
	//this.log.debug("Section Lookup: search for: " + queryVal);
	
	//inspect section for the section.state.id syntax. if found, use that to find section.
	var result = Section.parseId(queryVal);
	if ( result != null ){
		var section =  this.sections.get(result[1]);
		if ( section != null ){
			return section;	
		}
	}
	else{
		//try to find section with name specified.
		var section = this.sections.get(queryVal);
		if ( section != null ){
			return section;	
		}
	}

	
	//try to find a member having supplied id
	//note that a member elementId must match the control's id
	var sectionlist = this.sections.listValues();
	for ( var i = 0;i<sectionlist.length;i++){
		var sect = sectionlist[i];
		if ( sect.containsId(queryVal) ){
			return sect;	
		}
	}
		
	//if none of those worked, try using the "lazy init" approach, in which
	//the id of the control provides the desired information.
	//this creates a new Section and SectionMember on the fly if necessary
	
	//questionable if this approach provides value in our case: it can be commented out
	//as a block if it is not needed later.
   /*
	var result = Section.parseId(queryVal);
	if ( result != null ){
		//this.log.warn("Parsing ID " + queryVal + " to attempt to find a section and sectionmember dynamically.");
		var sectname = result[1];

		var id = result[0];
		var section = this.sections.get(sectname);
		if ( section == null ){
			//a new section will be created on the fly
			section = new Section(sectname);
			section.init();
			//this.log.warn("Created " + sectname + ". THIS IS PROBABLY A PROBLEM, because the new section has default states, and no initial state.");
		}
		return section;			
	}
	*/
	return null;
}

/**
 * method that finds all of the sections that match the provided regular expression.
 * Currently used only by setSectionStates, but coudl be used by other methods in the future
 */
Page.prototype.findSections = function ( regex ){
	
	//try to find a member having supplied id
	//note that a member elementId must match the control's id
	var sectionlist = this.sections.listValues();
	var matches = new Array();
	
	var regex = eval ( "/" + regex + "/" );
	
	for ( var i = 0;i<sectionlist.length;i++){
		var sect = sectionlist[i];
		if ( sect != null && sect.name != null ){
			var result = sect.name.match ( regex );
			if ( result != null ) {
				matches.push ( sect );			
			}
		}
	}
	return matches;		
}
 
/**
 * method that allows setting all sections that match a particular string
 * to a given state.
 * This is useful when there are many like-named sections on a page, and
 * it is desired to change all of them to a particular state.
 * @param sectname, the name of section(s) to set.  All sections starting with the specified
 * string will be set.
 * @param newstate, the new state. If a section does not have the specified state, it is ignored.
 */
Page.prototype.setSectionStates = function( sectregex, newstate ){

	//try to find a member having supplied id
	//note that a member elementId must match the control's id
	var sectionlist = this.findSections( sectregex );	
	for ( var i = 0;i<sectionlist.length;i++){
		var sect = sectionlist[i];
		sect.setState ( newstate );
	}	
}

/**
 * shortcut to find a section member given a page object.
 * Calling this method is equivalent to calling page.getSection(obj).getMember(obj).
 * @param the object to use for lookups
 * @return a section member, or null if the section or member is not found.
 */
Page.prototype.getMember = function ( obj ){
	var section = this.getSection(obj );
	
	if ( section != null ){
		return section.getMember(obj);
	}	
}

/*a SectionMember is a member of a section, and represents a single
 * page element that has a particular behavior in the context of a section.
 * 
 * each member of a section has
 *   - a reference to the id of the control the member represents
 *   - a variable that determines if the item is currently visible or not
 *   - a variable that indicates which state(s) the item should be visible in
 *   - a reference to its parent section
 *   - methods for showing and hiding the member
 * Section members can currently be two types of things:
 *   - an element that can be hidden/shown
 *   - a checkbox control
 * @param the id of the page element that the SectionMember represents
 * @constructor
 *
*/
function SectionMember( id, visibleStates){
	this.elementId = id;
	this.visible = null;
	this.modifyOnInit = true;
	this.parentSection = null;

	
	this.visibleStates = "show,visible,on";
	
	//override with a value if provided	
	if ( visibleStates != null ){
		this.visibleStates = visibleStates;
	}

	//this.log = Logger.getLogger("SectionMember");
	//extended attributes that are normally not necessary
	this.visibleStyle = "";
	this.invisibleStyle = "none";
	this.styleProperty = "style.display";
	
	//this.visibleStyle = "visible";
	//this.invisibleStyle = "hidden";
	//this.styleProperty = "style.visibility";
	this.multiStateDelimiter = "||";
}
/**
 * Utility method for resoving a value that's possibly null, a string ,  or a boolean into a boolean
 */
SectionMember.parseBoolean  = function(boolValue){
	if ( boolValue == null ){
		return false;
	}
	else{
		if ( boolValue == "false" || boolValue == "False" ){
			return false;
		}
		else{
			return true;
		}
	}
	
}
/**
 * @return the page element represented by the given id, or null if the id
   does not exist in the document
 */
SectionMember._findElement = function(id){
	var member = document.getElementById(id);
	if ( member != null ){
		return member;
	}
	else{
		return null;
	}
}

/**
 * initialize the section member. Provided in case subclasses wish
 * to perform operations when the section member is initialized.
 *
 */
SectionMember.prototype.init = function(){
	//do nothing	
	//this.log.debug(this.elementId + " : init()...");
	
	
	if ( 	SectionMember.parseBoolean(this.modifyOnInit)  ){
		var initstate = this.parentSection.initstate;		
		if ( initstate != null && initstate.length > 0 ){
			//this.log.info("SectionMember" + this.elementId + " setting initial state:" + initstate);	
			this.setState( initstate );
		}
		
	}
	else{
		//this.log.info("SectionMember" + this.elementId + " modifyOnInit is false, no state change will happen." );
	}	
}

/**
 * Notifies the seciton member that the parent section is 
 * changing state.  The new state is supplied as the function
 * argument.
 *
 * In response to this message, the sectionmember should change
 * its state as necessary
 * @param newState, the new state for the section.
 
 * Note that the parent section prevents unnecessary state changes--
 * only changes of state will result in this method being called.
 */
SectionMember.prototype.setState = function(newState){

	//this.log.debug(this.elementId + ": setting state " + newState );
	
	//disable if browser doesnt support well enough
	if ( ! BrowserUtils.isSupportedBrowser() ){
		return;
	}
	
	var vstates = this.visibleStates.split(this.multiStateDelimiter);
	//this.log.debug(this.elementId + ": visible states are:[" + vstates.toString() + "]");
	if ( vstates.contains(newState) ){
		this.show();		
	}
	else{
		this.hide();
	}
}

/**
 * Show the section member. This method queries the styleProperty attribute,
 * and sets the underlying page element to the desired state. basically
 * it calls:
 *    page_element.styleProperty = visibleStyle
 
 * The typical ( and default ) values are styleProperty="style.display" and
   visibleStyle="inline", resulting in inline display of an element.
 *
 */
SectionMember.prototype.show = function(){
	//this.log.debug("showing control");
	var member = SectionMember._findElement(this.elementId);
	if ( this.visible == null || !this.visible ){
		this._changeElementState(member,this.styleProperty,this.visibleStyle);	
	}
}

/**
 * Show the section member. This method queries the styleProperty attribute,
 * and sets the underlying page element to the desired state. basically
 * it calls:
 *    page_element.styleProperty = invisibleStyle
 
 * The typical ( and default ) values are styleProperty="style.display" and
   invisibleStyle="none", resulting hiding an element.
 *
 */
SectionMember.prototype.hide = function(){
	//this.log.debug("hiding control");
	var member = SectionMember._findElement(this.elementId);
	if ( this.visible == null || this.visible ){
		this._changeElementState(member,this.styleProperty,this.invisibleStyle);	
	}
}

/**
 * Internal method used to change the state of an element.
 * Uses browserUtils to make sure that the browser supports our stuff well enough for this to work.
 * @private
 */
SectionMember.prototype._changeElementState = function(obj,theProp,theValue){
	if ( ! BrowserUtils.isSupportedBrowser() ){
		return;
	}
	
	if ( obj != null ){
		//this.log.warn("Setting element " + obj.id + " [" + theProp + "]= " + theValue );
		if(theValue == ''){
			eval("obj."+theProp+"='"+theValue+"'");
	  }
	  else if (theValue == true || theValue == false){
			eval("obj."+theProp+"="+theValue);
		}
		else{
			eval("obj."+theProp+"='"+theValue+"'");
		}
	}
}




 /*
   a section represents a portion of a page that can change state

   a section knows its current state, provides methods
   to open and close the section, etc

		
	Section provides a method, setState(controlobj, state) that can change the state of a section. When it
	is executed, the following algorithm is performed:
	
		(1) the sectionname is determined from the controlobject
		(2) all of the items in the section are scanned.  All items with indexes from 1 - 20 are
		    scanned, and are manipulated in order from 1-10.
		(3) items who's state matches the desired state are turned on, all others are turned off
		
		
	A section requires its section members to support the following interface:
	
		a property called elementId
		a method called setState
		a method called init()
*/

/**
 * basic constructor
 * @constructor
 */
function Section(){;}

Section.DEFAULTSTATES = new Array("hide","show");
Section.log = Logger.getLogger("SectionClass" );
Section.STATE_CLOSED = "closed";
Section.STATE_OPEN = "open";
Section.MAX_MEMBER_INDEX=20;
Section.SEPARATOR=".";

/**
 * Create a new section with the specified name and initial state.
 * @param name the name of the section
 * @param istate the initial state of the section
 * @constructor
 */
function Section(name, istate ){
	this.name = name;
	this.state = null;
	this.initstate = istate;
	this.states = Section.DEFAULTSTATES;
	//this.log = Logger.getLogger("Section" );
	this.members = new Map();

	Section.log.debug("Created section " + name );			
}

/**
 * add an already craeted member to the section. 
 * the specified object must conform to the SectionMember interface
 * @param member the new member to add
 *
 */
Section.prototype.aM = function(member ){
	this.members.put(member.elementId,member);
	member.parentSection = this;
}

/**
 * Parse a string to determine if it contains
 * encoded section and state information.
 * @param objid a string to parse for section member format
 * @returns a regext result object, or null if there was no match
 */
Section.parseId = function ( objid ){
	var regex = /(\w+)\.(\w+)\.*(\w+)*/;
	var result = objid.match(regex);
	return result;
}


/**
 * Scans the document for members of the section
 * that have not been added explicly.
 
 * This method expects section members to follow this format 
 * for their Id attribute:
 *   <sectionname>.<statename>.<index>
 * 
 * The method will permute all of the valid state names with the section's
 * name, and search up to Section.MAXINDEX for section members.
 * This algorithm is necessary because documents do not supply a query
 * facility that allows "like" searches, and scanning all of the document
 * elements is too expensive.
 * 
 * for example:
 *     a section having name "section1" and states "hidden" and "visible"
 * would result in a search for controls with the following IDs:
 *              section1.hidden.1
 * 				 section1.visible.1
 *              section1.hidden.2
 *              section1.visible.2
 *                 ...
 *              etc
 *  discoverMembers is called automatically just before a section initializes. 
 *  Using autodiscovery to discover section members has the advantage that
 *  no javascript init code is required, but there are some disadvantages:
 *    -- only one visible state may be specified
 *    -- section members that are visible in all states cannot be found
 */
Section.prototype.discoverMembers = function(){

	var thiscontrol = null;
	var thisname = null;
	
	var searchStates = this.states;
	
	//TODO: search for the "all" or "none" special states
	//If found, add members as always on or always off
	
	for ( var j=0;j<searchStates.length;j++ ){
		for ( var i=1;i<=Section.MAX_MEMBER_INDEX;i++ ){
			thisname = this.name + Section.SEPARATOR + searchStates[j] + Section.SEPARATOR + i

			
			//this.log.debug("looking for control :" + thisname );
			thiscontrol = document.getElementById(thisname);
			if ( thiscontrol != null ){
				//this.log.info("found control " + thisname );
				//this.log.info("control id is " + thiscontrol.id );
				
				//one of the limitations of dynamically discovered items is here:
				//it can only have one visible state
				if ( ! this.members.containsKey(thiscontrol.id ) ){
					var type = thiscontrol.type;

					if ( type == "select-one" || type == " select-multiple" ){
						//this.log.info("adding combo box for control  " + thiscontrol.id );
						this.aCB(thiscontrol.id);	
					}
					else if ( type == "checkbox" ){
						//this.log.info("adding checkboxbox for control  " + thiscontrol.id );
						this.aChB(thiscontrol.id );	
					}
					else{
						//this.log.info("adding onoff member for control  " + thiscontrol.id );
						this.aNM(thiscontrol.id,searchStates[j] );	
					}						
				}
			}
			else{
				break;
			}
		}		
	}
	
}

/**
  * @param memberiD the id of the member to add
  * @param visible states, comma delimited list of states in which the 
  * member is visible
  *convienience method to add a member with typical options in one step
	TODO: how to make this overloaded with aM?
 */
 
Section.prototype.aNM = function(memberId,visiblestates,modifyOnInit ){
	
	var newmem = new SectionMember(memberId);
	

	if ( visiblestates != null ){
		newmem.visibleStates = visiblestates;
	}
	
	if ( modifyOnInit != null ){
		newmem.modifyOnInit = modifyOnInit;	
	}
	this.aM(newmem);		
}

/**
 * add a combo box member 
 * @param memberId, the id of the combo box to add
 */
Section.prototype.aCB = function(memberId,visiblestates,modifyOnInit ){
	if ( memberId == null ){
		//this.log.error("aCB: attempt to add null memberId");
		return;	
	}
	var member = new SectionComboBox ( memberId,visiblestates );
	if ( visiblestates != null ){
		member.visibleStates = visiblestates;
	}
	if ( modifyOnInit != null ){
		member.modifyOnInit = modifyOnInit;	
	}
	this.aM(member);
}

/**
 * add a state combo box member 
 * @param memberId, the id of the combo box to add
 */
Section.prototype.aSCB = function(memberId,visiblestates,modifyOnInit  ){
	if ( memberId == null ){
		//this.log.error("aCB: attempt to add null memberId");
		return;	
	}
	var member = new StateSelectComboBox ( memberId );
	if ( modifyOnInit != null ){
		member.modifyOnInit = modifyOnInit;	
	}	
	this.aM(member);
}

/**
 * Add a checkbox to the section
 * @param memberId, the check box to add
 * @param initialState, the initial state either checked or unchecked.
 * This method is designed to be used with Sections having states
 * "checked" and "unchecked"
 */
Section.prototype.aChB = function(memberId,visiblestates,modifyOnInit ){
	if ( memberId == null ){
		//this.log.error("aChB: attempt to add null memberId");
		return;	
	}
	var member = new SectionMember(memberId);
	member.styleProperty="checked";
	member.visibleStyle = true;
	member.invisibleStyle = false;
	member.visibleStates = "checked";
	if ( modifyOnInit != null ){
		member.modifyOnInit = modifyOnInit;	
	}	
	this.aM ( member );
}

/**
 * Search the members for this section, and return a section
 * member for the supplied object if one exists
 * @param obj, either an object or object id to look for
 * @returns either a section member or null if one was not found.
 */
Section.prototype.getMember = function(obj ){
	var queryVal = null;
	if ( obj != null && obj.id != null ){
		queryVal = obj.id;
	}
	else{
		queryVal = obj;
	}
	
	return this.members.get(queryVal);
}

/**
 * @returns true if the specified memberId is represented by a section member
 * of this section, false otherwise
 */
Section.prototype.containsId = function(memberId ){
	return this.members.containsKey(memberId );
}

/**
 * Adds a state to the list of valid states for this section.
 * @param newState the state to add.
 * 
 * The default states for a section are "hide" and "show" 
 * it is also possible to direclty modify the states using the states property
 */
Section.prototype.addState = function ( newState ){
	this.states.push(newState);	
}

/**
 * Initialize the section. When sections are initialzed, they perform
 * the following steps:
 *     (1) auto-discover members that exist in the document that were not
           already added
       (2) determine the desired initial state of the section
       (3) call the init() method of all section members
       (4) set the section's state to the desired initial state, which
           results in calling the setState method of all section members
 */
Section.prototype.init = function (){
	//init all members
	
	//discover members if we dont seem to have any

	//this.log.info("Section" + this.name + "doesnt seem to have any members. I'll try to find some.");
	//this.discoverMembers();	

	var memlist = this.members.listValues();
	//this.log.info("Section" + this.name + " initializing " + memlist.length + " members." );	
	for ( var i=0;i<memlist.length;i++){
		memlist[i].init();	
	}
	
	this.state = this.initstate;
	//removed: section members are now responsible for initializing themselves.
	//if ( this.initstate != null && this.initstate.length > 0 ){
	//	//this.log.info("Section" + this.name + " setting initial state:" + this.initstate);	
	//	this.setState( this.initstate );
	//}
}

/**
 * Determine if the provide state is valid for this section
 * @param state, the state to check
 * @returns true if the state is in the sections' state list, false otherwise.
 */
Section.prototype.isValidState = function ( state ){
	for ( var i=0;i<this.states;i++){
		if ( this.states[i] == state ) return true;	
	}
	return false;
}

/**
 *  Set the state of the section to a new state.
 *  When this method is called, all of the section members receive a 
 * method call to their setState method.
 * 
 * This method checks the current state to prevent redundant state changes
 * and changes to states that are not acceptable to the section.
 * @param newState the new state to activate.
 * @param forceSet forces to set the state even if it is the current one
 *
 * The method also updates a hidden form property, if the form is available,
 * that will transmit to the server what the user's selected section state was
 */
Section.prototype.setState = function ( newState, forceSet ){

	//disable if browser doesnt support well enough
	if ( ! BrowserUtils.isSupportedBrowser() ){
		return;
	}	
	
	if ( forceSet == 'undefined' || !forceSet ) {
		if ( newState == this.state ){
			//this.log.warn("Received request to change state to existing state " + newState + " ignored." );
			return;
		}
	}
	
	if ( ! this.isValidState(newState) ){
		//this.log.warn("Received request to change state to invalid state " + newState + " ignored." );			
	}
	
	//this.log.info("setting state to new state: " + newState );
	


	var memberlist = this.members.listValues();
	//this.log.info("setstate: found  " + memberlist.length + " members" );
	var mem = null;
	for ( var j = 0;j< memberlist.length;j++ ){
		mem = memberlist[j];
		//this.log.debug("control id is " + mem.elementId );
		mem.setState(newState);
	}	

	this.state = newState;	
	
	Page.currentPage.setFormValue("section(" + this.name + ").currentStateString" ,newState );		
}

/**
 *
 * toggles the state from one to the other. does nothing if there are more
 * than two states defined.
 */
Section.prototype.toggleState = function(){
	if ( this.states.length != 2 ){
		//this.log.warn("Section" + this.name + " cannot be toggled. No action taken.");	
	}
	else{
		if ( this.states[0] == this.state ){
			this.setState(this.states[1]);	
		}
		else{
			this.setState(this.states[0]);
		}
	}
	
}



/**
 * A combo box that changes the parent section's state to the state
 * given by the value attribute of the option selected.
 * This is a case in which the section has a state defined for each
 * option in the DDLB.
 * @param objId the object id of the combo box
 * @constructor
 */
function StateSelectComboBox ( objId ){
	//create a new combo box.
	this.elementId = objId;	
	this.parentSection = null;
	//this.log = Logger.getLogger("StateSelectComboBox" );
	//store a reference to a combo box object
	
}

StateSelectComboBox.log = Logger.getLogger("StateSelectComboBox" );

/**
  *initialize the box. In this case, we add an option to the list
 */
StateSelectComboBox.prototype.init = function(){
	//this.log.warn("StateSelectComboBox: " + this.elementId + " : init() ");
	this.box = document.getElementById(this.elementId);
	
	
	if ( 	SectionMember.parseBoolean(this.modifyOnInit)  ){
		var initstate = this.parentSection.initstate;		
		if ( initstate != null && initstate.length > 0 ){
			//this.log.info("SectionMember" + this.elementId + " setting initial state:" + initstate);	
			this.setState( initstate );
		}
		
	}
	else{
		//this.log.info("SectionMember" + this.elementId + " modifyOnInit is false, no state change will happen." );
	}
		
}

/**
 * set the state. 
 */
StateSelectComboBox.prototype.setState = function ( newState ){
	//no action required on setState
}

/**
 * change state of the parent to the desired state
 */
StateSelectComboBox.prototype.onChange = function (){

	//disable if browser doesnt support well enough
	if ( ! BrowserUtils.isSupportedBrowser() ){
		return;
	}
		
	//change the parent section's state to the 
	//one specified by the value of the option. ( yes this is really it!)
	
	//bugfix: if not initialized, try to find element on the fly.
	//this code allows this object to work, even if init has not been called
	if ( this.box == null ){
		this.box = document.getElementById(this.elementId);
	}
	
	if ( this.box != null ){
		this.parentSection.setState(this.box.value );	
	}
	else{
		//this.log.warn("StateSelectComboBox: couldnt find target ddlb.");
	}
	
}