// The global array of objects that have been instanciated
if (!Bs_Objects) {var Bs_Objects = [];};

/**
* JavaScript Editable Dropdown Component.
* Browsers don't offer this functionality yet (mid-2003).
* 
* <b>Features:</b> 
* - IE6 and NS(Mozilla7) compliant.
* - Simulates a normal HTML-form text input field; so your form-hanling will not change.
* - Works over frames: Imagine you have a top frame (for a toolbar) and a main frame for the content.
*   Now in the top frame you place a dropdown. it should go over the bottom frame; this works 
*   with this class, but not with domapi.
* 
* <b>Includes (+Dependences):</b>
* <code>
*   <script type="text/javascript" src="/_bsJavascript/core/lang/Bs_Misc.lib.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/lang/Bs_Array.class.js" ></script>
*   <script type="text/javascript" src="/_bsJavascript/core/form/Bs_FormUtil.lib.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/form/Bs_FormFieldSelect.class.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/components/Bs_Dropdown.class.js"></script>
* </code>
* 
* <b>How to use:</b>
* 1. Have a look at the example (see example link below)
* 2. Create a function in the HTML-header called init(). Place the javascript code 
*    that instanciates and inits this component into init().
* 3. Place an 'onLoad' in your body-tag: e.g. <body onLoad="init();">
* 4. In the HTML body: Place a div- or span-tag with an unique ID where you want the componet to be.
* 
* <b>How it works:</b>
* - [After instanciating and initializing the object]. Call the drawInto([tag id]) methode.
*   This will search the HTML code for a tag with the given id and insert HTML- and JS- code
*   dynamically (using DOM) to display the component and handle it.
* - This class generates a normal HTML text input field and saves the value in there.
*   When used in a form, it will be submitted like any other HTML-form-field.
* 
* <b>What is returned to the server:</b>
*<pre>
*  +------------------------+--------------------------------------------------+
*  | DropdownName +"_select"| string, What the user selected                   |
*  +------------------------+--------------------------------------------------+
*  | DropdownName +"_input" | string, If no changes were made, same as above;  |
*  |                        | otherwise what the user filed in.                |
*  +------------------------+--------------------------------------------------+
*</pre>
*
* <b>Event-Handling</b> (see {@link attachEvent()}):
* - You can attach code or a callback function for certain events onto this component
*   (events are things like onClick, onChange ... aso) See attachEvent() for a list 
*   of supported events and more details.
*
* Snipit 1) Attaching a callback function:
* <code>
*     var myDropDown_1 = new Bs_Dropdown();
*     myDropDown_1.attachEvent('onChange', yourCallbackFunctionName);
* </code>
* The function you pass should be able to receive a object as parameter.
* The object is an instance of this class and allows you to access it the 
* component that triggered the event:
* <code>
*     function yourCallbackFunctionName(componentObj){
*       // do some stuff.
*     }
* </code>
* = = = =<br>
* Snipit 2) Attaching JS code:
* <code>
*     var checkBox = new Bs_Checkbox();
*     checkBox.attachOnClick("yourFunctionName('foo', 'bar'); alert('hello');");
* </code>
*
* <b>Customized images:</b>
* - Set the path to your image-dir {@link imgDir}
* - Following images must be created.
* <pre>
*     arrow.gif
* </pre>
*
* @example components/dropdown/examples/example1.php Simple example
* 
* @author     andrej arn <andrej-at-blueshoes-dot-org>
* @package    javascript_components
* @subpackage dropdown
* @copyright  blueshoes.org
*/
function Bs_Dropdown() {
	
  /**
  * When submitting the form, you'll receive the date value under this name.
	* 
	* In other words you'll receive the data back to the server as if you had placed <br>
  * <code><input type=text name="[the fieldName]" id="[the fieldName]"  value="[the value]"></code><br>
	* into your HTML-form. 
	* 
	* that is when you use the drawInto() method. with convertField() the original field name is kept.
	* 
	* @access public
	* @var    string
	*/
	this.fieldName;
	
	/**
  * Unique Object/Tag ID is initialized in the constuctor.
  * Based on this._id. Can be used in generated JS-code as ID. Is set together 
  * from the  classname + this._id (see _constructor() code ).
  *
  * @access private
  * @var  string 
  */
  this._tagId;

  /**
  * If set to TRUE, a new text string gets automatically added to the select list.
  * default is true.
  * @access public
  * @var    bool
  */
  this.autoAdd = true;
  
	/**
	* if you want newly added elements to be stored in a cookie so that 
	* if the page is reloaded, or the user comes back, ... the user-typed 
	* option is in the list as well. without the need to code something 
	* server-side to include the option.
	* 
	* @access public
	* @var    string rememberNewInCookie
	* @see    var this.autoAdd
	* @since  bs4.5
	*/
	this.rememberNewInCookie;
	
	/**
	* array list of the user-typed elements stored in the cookie.
	* @access private
	* @var    array _cookieElements
	* @see    this.rememberNewInCookie
	*/
	this._cookieElements = new Array();
	
	/**
	* the max length (chars) values can have. not set means no limit.
	* @access public
	* @var    int maxLength
	*/
	this.maxLength;
	
  /**
  * How the options in the select field should be ordered.
  * <pre>
  *   1 = new options at the end (default)
  *   2 = new options at the begin
  *   3 = alphabetically descending
  *   4 = alphabetically ascending
  * </pre>
  * @access public
  * @var    int
  * @todo   code support for this.
  */
  this.selectOrder = 1;
  
  /**
  * Dropdown can be disabled. then it's value cannot be changed.
  * @access public
  * @var    bool (default is false)
  * @see    guiNochange
  */
  this.disabled = false;
  
  /**
  * If set to true then the user cannot change things by clicking. 
  * Changes through the api functions (for the coder, you) are still possible.
  * @access public
  * @var    bool 
  * @see    disabled
  */
  this.guiNochange;
  	
  /**
  * Directory where the images are. 
	* Feel free to create your own images, put them into another directory (by using the same file names, etc.)
  * See header for more info.
  * @access public
  * @var string 
  */
  this.imgDir = '/_bsJavascript/components/dropdown/img/win2k/';
  
  /**#@+
	* Customize look. When using a CSS you may add a CSS class name that will be used when rendering the field.
	* @access public
  * @var string 
	*/
  this.classInput;
  this.classSelect;
	/**#@-*/
  
	/**
  * Reference to the text field object.
  * WARNING: use _getTextObj() to access this, cause it may not have been set yet!
  * @access private
  * @var    object
  * @see    _getTextObj()
  */
  this._fromTextObj;
  
  /**
  * Reference to the select field object.
  * WARNING: use _getSelectObj() to access this, cause it may not have been set yet!
  * @access private
  * @var    object
  * @see    _getSelectObj()
  */
  this._frmSelectObj;
  
  /**
  * array holding all the information about attached events. 
  * the structure can be like these:
  * <pre>
  * 1) attach a function directly
  *    syntax:  _attachedEvents['eventName'] = yourFunctionName;
  * 2) attach some javascript code
  *    syntax:  _attachedEvents['eventName'] = "yourCode();";
  *    example: _attachedEvents['eventName'] = "alert('hi'); callSomething('foo');";
  *    just keep in mind that you cannot use vars in that code, because when it 
  *    gets executed that will be another scope (unless the vars are global...)
  * 3) attach multiple things for the same event
  *    syntax:  _attachedEvents['eventName']    = new Array;
  *             _attachedEvents['eventName'][0] = yourFunctionName;
  *             _attachedEvents['eventName'][1] = "yourCode();";
  * </pre>
  * 
  * @access private
  * @var    array (hash, see above)
  * @see    attachEvent();
  */
  this._attachedEvents;  
  
  /**
  * Tells if this object is already rendered. then changes need to be done 
  * differently (live on the page).
  * @access private
  * @var    bool
  */
  this.isOutRendered = false;
  
  /**
  * @access private
  * @var    string
  * @see    setValue(), getValue()
  */
  this._value = '';
  
  /**
  * The selectable options.
  * @access private
  * @see addOption()
  * @var array (vector)
  */
  this._options = new Array();
  
	
	/**
	* helper var for the toSelect() method.
	* @access private
	* @var    bool _firstTimeToSelect
	*/
	this._firstTimeToSelect = true;
	
	/**
	* vector with 2 elements, 0=value, 1=unit.
	* @access private
	* @var    array _size
	* @see    this.setSize()
	*/
	this._size;


	/**
	* the pseudo constructor.
	* @access private
	* @return void
	*/
	this._constructor = function() {
  	// Put this instance into the global object instance list
    this._id = Bs_Objects.length;
    Bs_Objects[this._id] = this; 
    this._tagId = "Bs_Dropdown_"+this._id+"_";
  }
  
	
  /**
  * Returns the current value.
  * @access public
  * @return string
  */
  this.getValue = function() {
    return this._value;
  }
  
  /**
  * If you wish to change the value of this component, call this method.
  * If the value is not an option yet, it will be added to the options array.
  * 
  * NOTE: 
	* - Attached onChange events will *not* fire.
  * 
  * @access public
	* @param  string value
	* @param  bool addToCookie (used internally only)
  * @return void
  * @see    addOption(), attachEvent()
  */
  this.setValue = function(value, addToCookie) {
		//alert(value); //4debug
    this._value = value;
    this.addOption(value, addToCookie);
    if (this.isOutRendered) {
      this._getTextObj().value  = value;
      this._getSelectObj().setTo(value);
			this._updateHiddenField();
    }
  }
  
  
  /**
  * Add an option to the list.
	* 
  * NOTE: 
	* - Options will be merged case-insensitive, otherwise we'd have too much crap.
  * 
  * @access public
  * @param  string str
	* @param  bool addToCookie (used internally only)
  * @return bool (true if it got added, false if it was already there.)
  * @see    attachEvent(), this.rememberNewInCookie
  */
  this.addOption = function(str, addToCookie) {
    //let's see if we already have such an option:
    if (this._options.indexOf(str) >= 0) {
      return false;
    } else {
      this._options[this._options.length] = str;
      if (this.isOutRendered) {
        var newOpt = new Option(str, str);
        this._getSelectObj().options[this._getSelectObj().length] = newOpt;
      }
			if (addToCookie && (typeof(this.rememberNewInCookie) == 'string')) {
				this._saveCookieString(str);
			}
	    return true;
    }
  }
	
	
	/**
	* sets the size (width) of the fields.
	* 
	* you can define the width by using css classes, see this.classInput 
	* and this.classSelect. that is a safe bet. note that the width 
	* of the input field should be the one of the select field -20 (for 
	* the arrow icon). example 1 demonstrates this:
	* http://www.blueshoes.org/_bsJavascript/components/dropdown/examples/example1.html
	* 
	* if you use an existing text field, and convert it, like in example 2 
	* http://www.blueshoes.org/_bsJavascript/components/dropdown/examples/example2.html
	* then the "size" attribute of your text field is used. note that if you use 
	* css classes, and they have a width defined, the browser ignores this "size". 
	* css is stronger.
	* 
	* if none of the above apply for your case, you can define the size by calling this 
	* method BEFORE rendering the dropdown. 
	* the param "value" is a number. the param "unit" can be "char" or "pixel".
	* examples:
	*   yourObj.setSize(40,  'char')    => the input field will use "size=40" as attribute.
	*   yourObj.setSize(200, 'pixel')   => the select field will use a style setting of 
	*                                      200px. the input field has one with 180px (-20 
	*                                      for the arrow icon).
	* 
	* @access public
	* @param  int    value
	* @param  string unit
	* @return void
	*/
	this.setSize = function(value, unit) {
		this._size = new Array(value, unit);
	}

  /**
  * Renders this component (generates html code).
	* 
  * We added this methode to the public API for the advanced user. It's half-private :).
  * This is a call for the advanced user, who would like to fetch the html- / js-
  * that is rendered to make this component running.<br>
	* The average user will normally use {@link drawInto()}, that will render and place the 
	* code into the site in one simple step.
	* 
	* @access public
  * @param  string tagId (id of the tag.)
  * @return string
  * @see drawInto()
	*/
  this.render = function(tagId) {
    
    if (!bs_isEmpty(tagId)) {
      this._tagId = tagId;
    }
		
		if (typeof(this.rememberNewInCookie) == 'string') {
			this._readCookie();
			for (var i=0; i<this._cookieElements.length; i++) {
				this.addOption(this._cookieElements[i], false);
			}
		}

    var out  = new Array();
    var outI = 0;
    
    out[outI++] = '<table border="0" cellspacing="0" cellpadding="0" style="vertical-align:middle; display:inline;"><tr><td align="right">';
		
		if (typeof(this.fieldName) == 'undefined') this.fieldName = this._tagId + 'value';
		
		//add the hidden field
    out[outI++] = '<input name="' + this.fieldName + '" id="' + this.fieldName + '" type="hidden" value="' + this._value + '">';
		
    out[outI++] = '<input name="' + this._tagId + '_input" id="' + this._tagId + '_input" type="text" value="' + this._value + '"';
		if ((typeof(this._size) == 'object') && (this._size[1] == 'char')) {
			out[outI++] = ' size="' + this._size[0] + '"';
		}
    out[outI++] = ' onblur="Bs_Objects['+this._id+']._textFieldOnBlur();"';
		out[outI++] = ' onkeydown="var _dd=Bs_Objects['+this._id+']; return _dd._textFieldOnKeyDown();"';
    if (!bs_isEmpty(this.classInput)) {
      out[outI++] = ' class="' + this.classInput + '"';
    }
    out[outI++] = ' style="border-right:none; margin-top:1px;';
		if ((typeof(this._size) == 'object') && (this._size[1] == 'pixel')) {
	    out[outI++] = ' width:' + (this._size[0] -20) + ';';
		}
    out[outI++] = '"';
    if (!bs_isEmpty(this.maxLength) && (this.maxLength >= 0)) out[outI++] = ' maxlength="' + this.maxLength + '"';
    out[outI++] = '>';
    out[outI++] = '<select name="' + this._tagId + '_select" id="' + this._tagId + '_select"';
		out[outI++] = ' onChange="Bs_Objects['+this._id+'].toInput();"';
		out[outI++] = ' onBlur="Bs_Objects['+this._id+'].toInput();"';
		out[outI++] = ' onDoubleClick="Bs_Objects['+this._id+'].toInput();"'; //does not fire :(
		//out[outI++] = ' onXXXMouseOut="' + this._tagId + '.toInput();"';
		out[outI++] = ' onkeydown="Bs_Objects['+this._id+']._selectFieldOnKeyDown();"';
		out[outI++] = ' style="display:none; margin-top:2px;';
		if ((typeof(this._size) == 'object') && (this._size[1] == 'pixel')) {
	    out[outI++] = ' width:' + this._size[0] + ';';
		}
    out[outI++] = '"';
    if (!bs_isEmpty(this.classSelect)) {
      out[outI++] = ' class="' + this.classSelect + '"';
    }
    out[outI++] = '>';
    for (var i=0; i<this._options.length; i++) {
      out[outI++] = '<option value="' + this._options[i] + '"';
			if (this._value == this._options[i]) {
				out[outI++] = ' selected'
			}
			out[outI++] = '>' + this._options[i] + '</option>';
    }
    out[outI++] = '</select>';
    
    out[outI++] = '</td><td>';
    out[outI++] = '<img id="' + this._tagId + '_arrow" src="' + this.imgDir + 'arrow.gif"';
    out[outI++] = ' onMouseOver="Bs_Objects['+this._id+'].toSelect();"';
    out[outI++] = ' style="margin-left:0px; margin-top:0px; border-top: 2px ridge #848284; border-right:1px solid #D6D3CE; border-bottom:1px solid #D6D3CE;"';
    out[outI++] = ' align="top"';
		out[outI++] = '>';
    out[outI++] = '</td></tr></table>';
    out[outI++] = '';
    
    this.isOutRendered = true;
    
    return out.join('');
  }
  
  /**
  * Renders the dropdown component and places it into the tag specified.
	* 
  * @access public
  * @param  string tagId id of the tag. (Hint: use a <div> or <span>.)
  */
  this.drawInto = function(tagId) {
    document.getElementById(tagId).innerHTML = this.render(tagId);
  }
  
	
	/**
	* takes an existing html form field (of type text) and converts it into 
	* a Bs_Dropdown field.
	* 
	* @access public
	* @param  string fieldId
	* @return void
	*/
	this.convertField = function(fieldId) {
		var origFld = document.getElementById(fieldId);
		if (origFld.tagName == 'INPUT') { //text field
			//if (!bs_isEmpty(origFld.value)) { //nah, do it even if it's empty. really. changed in bs4.5 --andrej
				this.setValue(origFld.value, false);
			//}
			if (!bs_isEmpty(origFld['size']))      this.setSize(origFld['size'], 'char');
			if (!bs_isEmpty(origFld['maxLength'])) this.maxLength = origFld['maxLength'];
			if (!bs_isEmpty(origFld['name'])) {
				this._tagId    = origFld['name'];
				this.fieldName = origFld['name'];
			}
		} else if (origFld.tagName == 'SELECT') {
			//not supported [yet].
		}
		origFld.outerHTML = this.render();
	}
	
  
  /**
  * @access private
  */
  this.mouseOutTimer = function() {
  }
  
  
  /**
  * Attaches an event like onChange, onMouseOver, onClickCaption ... a.s.o.
  * Supported events are:
  * - 'onChange'
  *
  * @access public
  * @param  string trigger (for example 'onChange')
  * @param  mixed A globalFunctionName OR string of javascript to be evaled (using JS's eval())
  * @return void
  * @see    _attachedEvents
  */
  this.attachEvent = function(trigger, yourEvent) {
    if (typeof(this._attachedEvents) == 'undefined') {
      this._attachedEvents = new Array();
    }
    
    if (typeof(this._attachedEvents[trigger]) == 'undefined') {
      this._attachedEvents[trigger] = new Array(yourEvent);
    } else {
      this._attachedEvents[trigger][this._attachedEvents[trigger].length] = yourEvent;
    }
  }
  
  /**
  * Tells if an event is attached for the trigger specified. 
  * @access public
  * @param  string trigger
  * @return bool
  */
  this.hasEventAttached = function(trigger) {
    return (this._attachedEvents && this._attachedEvents[trigger]);
  }
  
  /**
  * Fires the events for the trigger specified.
  * (used internally, but feel free to trigger events yourself...)
  * @access public
  * @param  string trigger (for example 'onClickCaption')
  * @return void
  */
  this.fireEvent = function(trigger) {
    if (this._attachedEvents && this._attachedEvents[trigger]) {
      var e = this._attachedEvents[trigger];
      if ((typeof(e) == 'string') || (typeof(e) == 'function')) {
        e = new Array(e);
      }
      for (var i=0; i<e.length; i++) {
        if (typeof(e[i]) == 'function') {
          e[i](this);
        } else if (typeof(e[i]) == 'string') {
          eval(e[i]);
        } //else murphy
      }
    }
  }
  
	
  /**
	* ????
	* 
	* looks like this is not used at the moment. the idea was to use a timer 
	* that resets the select field to a text field.
	* 
  * @access private
  */
  this.activeTimer = function() {
    //alert(document.activeElement.id);
    if (typeof(document.activeElement.id) == 'string') {
      if (
        (document.activeElement.id == (this._tagId + '_select'))
        || 
        (document.activeElement.id == (this._tagId + '_input'))
      ) {
        return; //everything is ok
      }
    }
    //hrm, let's switch back.
    this.toInput();
    //alert(document.activeElement.id);
  }
  
  
  /**
  * Hides the input field, shows the select field.
  * @access private
	* @return void
  * @see    toInput()
  */
  this.toSelect = function() {
		var textElm   = this._getTextObj();
		var selectElm = this._getSelectObj();
		var textWidth = textElm.offsetWidth;
		
    textElm.style.display  = 'none';
    document.getElementById(this._tagId + '_arrow').style.display  = 'none';
		
		if (this._firstTimeToSelect) {
			if (bs_isEmpty(this.classSelect)) {
				selectElm.style.width   = (textWidth + 20) + 'px';
			}
			this._firstTimeToSelect = false;
		}
		
    selectElm.style.display = 'block';
		selectElm.focus();
    //this._getSelectObj().click(); //crap, select fields don't support that.
  }
  
  /**
  * Hides the select field, shows the input field.
  * @access private
	* @return void
  * @see toSelect()
  */
  this.toInput = function() {
    this._getSelectObj().style.display = 'none';
    document.getElementById(this._tagId + '_arrow').style.display  = '';
    this._getTextObj().value          = this._getSelectObj().getValue();
    if (this._getTextObj().value == 'undefined') this._getTextObj().value = '';
    this._getTextObj().style.display  = '';
    //this._getTextObj().select();
    //this._getTextObj().focus();
    if (this._value != this._getTextObj().value)  {
      this._value = this._getTextObj().value;
			this._updateHiddenField();
      if (this.hasEventAttached('onChange')) this.fireEvent('onChange');
    }
  }
	
	
	/**
	* updates the value of the hidden field.
	* @access private
	* @return void
	* @since  bs4.4
	*/
	this._updateHiddenField = function() {
		document.getElementById(this.fieldName).value = this._value;
	}
	
	/**
	* Fires when the text input field loses focus. used internally.
	* @access private (used with an onBlur event attached to the text field, don't fuck with it.)
	* @return void
	* @since  bs4.4
	*/
	this._textFieldOnBlur = function() {
    var txtVal = this._getTextObj().value;
    if (this._value != txtVal) {
	    if (this.autoAdd) {
  	    this.setValue(txtVal, true); //sets internal value, and adds the option to the list.
			} else {
				this._value = txtVal; //only set the internal value.
			}
			this._updateHiddenField();
			this.fireEvent('onChange');
		}
	}
	
	
	/**
	* Fires on typing in the text input field.
	* @access private (used with an onkeydown event attached to the text field, don't fuck with it.)
  * @return void
	* @since  bs4.4
	*/
	this._textFieldOnKeyDown = function() {
    //alert(window.event.keyCode);
    try { //ie-only i think
			switch (window.event.keyCode) {
				case 13: //enter
					//make that change active:
					this._textFieldOnBlur(); //call it instead of copying the code over.
					return false;
					break;
				case 27: //esc
					//back to what it was before:
					this._getTextObj().value = this._value;
					return false;
					break;
		    case 40: //cursor-down
					this.toSelect();
					//this._getSelectObj().focus();
					return false;
					break;
			}
		} catch (e) {
			//never mind
		}
		return true;
	}
	
	/**
	* Fires on typing in the select input field.
	* @access private (used with an onkeydown event attached to the select field, don't fuck with it.)
	* @return void
	* @since  bs4.4
	*/
	this._selectFieldOnKeyDown = function() {
    //alert(window.event.keyCode);
    try { //ie-only i think
			switch (window.event.keyCode) {
				case 27:  //esc
				case 113: //F2
					//switch from select to input mode:
					this.toInput();
					break;
			}
		} catch (e) {
			//never mind
		}
	}	
	
	
	/**
	* reads the user-typed elements that were previously stored as cookie.
	* @access private
	* @return void
	*/
	this._readCookie = function() {
		var cookieName   = "bsdd_" + this.rememberNewInCookie;
		var list = new Array();
		if (document.cookie) {
			var allCookies = document.cookie.split('; ');
			for (var i=0; i<allCookies.length; i++) {
				if (allCookies[i].substr(0, cookieName.length) == cookieName) {
					myCook = allCookies[i];
					var Wertstart = myCook.indexOf("=") + 1;
					if (Wertstart == 0) break;
					var Wertende = myCook.indexOf(";");
					//alert(document.cookie);
					if (Wertende == -1) Wertende = document.cookie.length;
					myListStr = myCook.substring(Wertstart, Wertende);
					var list = myListStr.split(',,,');
				}
			}
		}
		this._cookieElements = list;
	}
	
	/**
	* saves the user-typed option in the cookie.
	* @access private
	* @param  string value (the new value to add)
	* @return void
	*/
	this._saveCookieString = function(value) {
		this._cookieElements[this._cookieElements.length] = value;
		var fullString   = this._cookieElements.join(',,,');
		var cookieName   = "bsdd_" + this.rememberNewInCookie;
		var lifetime     = 1000*60*60*24*365;
		var dateNow      = new Date();
		var dateEnd      = new Date(dateNow.getTime() + lifetime);
		var cookieString = cookieName + "=" + fullString + "; expires=" + dateEnd.toGMTString() + ";";
		document.cookie  = cookieString;
	}
  
  
  /**
  * ???
  * @access private
  * @return object
  */
  this._getTextObj = function() {
    if (typeof(this._fromTextObj) != 'object') {
      this._fromTextObj = document.getElementById(this._tagId + '_input');
    }
    return this._fromTextObj;
  }
  
  /**
  * ???
  * @access private
  * @return object
  */
  this._getSelectObj = function() {
    if (typeof(this._frmSelectObj) != 'object') {
      this._frmSelectObj = document.getElementById(this._tagId + '_select');
      //extend the select field object with some functionality. hacky javascript thingie:
      var tmp = new Bs_FormFieldSelect();
      tmp.init(this._frmSelectObj);
    }
    return this._frmSelectObj;
  }
  
	this._constructor(); //call the constructor. needs to be at the end.
	  
}

